แนวทางปฏิบัติที่ดีที่สุดในการใช้ HttpClient ในสภาพแวดล้อมแบบมัลติเธรด


85

ในขณะที่ฉันใช้ HttpClient ในสภาพแวดล้อมแบบมัลติเธรด สำหรับทุกเธรดเมื่อเริ่มต้นการเชื่อมต่อเธรดจะสร้างอินสแตนซ์ HttpClient ใหม่ทั้งหมด

เมื่อเร็ว ๆ นี้ฉันได้ค้นพบว่าการใช้วิธีนี้อาจทำให้ผู้ใช้เปิดพอร์ตมากเกินไปและการเชื่อมต่อส่วนใหญ่อยู่ในสถานะ TIME_WAIT

http://www.opensubscriber.com/message/commons-httpclient-dev@jakarta.apache.org/86045.html

ดังนั้นแทนที่จะทำแต่ละเธรด:

HttpClient c = new HttpClient();
try {
    c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
}

เราวางแผนที่จะมี:

[วิธีการ A]

// global_c is initialized once through
// HttpClient global_c = new HttpClient(new MultiThreadedHttpConnectionManager());

try {
    global_c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
}

ในสถานการณ์ปกติ global_c จะถูกเข้าถึงโดย 50 ++ เธรดพร้อมกัน ฉันสงสัยว่าสิ่งนี้จะสร้างปัญหาด้านประสิทธิภาพหรือไม่ MultiThreadedHttpConnectionManager ใช้กลไกที่ไม่มีการล็อกเพื่อใช้นโยบายความปลอดภัยของเธรดหรือไม่

ถ้า 10 เธรดใช้ global_c เธรดอีก 40 เธรดจะถูกล็อกหรือไม่

หรือจะดีกว่าถ้าในทุกเธรดฉันสร้างอินสแตนซ์ของ HttpClient แต่ปล่อยตัวจัดการการเชื่อมต่ออย่างชัดเจน

[วิธีการ B]

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManager();
HttpClient c = new HttpClient(connman);
try {
      c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
    connman.shutdown();
}

connman.shutdown () จะประสบปัญหาด้านประสิทธิภาพหรือไม่

ฉันขอทราบวิธีการ (A หรือ B) ที่ดีกว่าสำหรับแอปพลิเคชันที่ใช้เธรด 50 ++

คำตอบ:


47

แน่นอนวิธี A เพราะมันรวมกันและด้ายปลอดภัย

หากคุณกำลังใช้ httpclient 4.x, จัดการการเชื่อมต่อที่เรียกว่าThreadSafeClientConnManager ดูรายละเอียดเพิ่มเติมในลิงก์นี้(เลื่อนลงไปที่ "การรวมตัวจัดการการเชื่อมต่อ") ตัวอย่างเช่น:

    HttpParams params = new BasicHttpParams();
    SchemeRegistry registry = new SchemeRegistry();
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    ClientConnectionManager cm = new ThreadSafeClientConnManager(params, registry);
    HttpClient client = new DefaultHttpClient(cm, params);


สวัสดี httpclient ที่สร้างโดยวิธีนี้สามารถใช้เพื่อรักษาเซสชันตามที่อธิบายไว้ที่นี่stackoverflow.com/questions/5960832/… ... ? เพราะเมื่อฉันพยายามฉันไม่สามารถรักษาเซสชันข้ามคำขอที่แตกต่างกันได้ ...
sakthig

17
4.3.1 ที่นี่: PoolingClientConnManager เลิกใช้แล้วเนื่องจาก PoolingHttpClientConnectionManager
Matthias

@DrewStephens อีกครั้ง PoolingClientConnManager เลิกใช้แล้วเพื่อสนับสนุน PoolingHttpClientConnectionManager
didxga

18

แนะนำวิธี A โดยชุมชนนักพัฒนา httpclient

โปรดดูhttp://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.htmlสำหรับรายละเอียดเพิ่มเติม


1
เมื่อไหร่จะเรียก "ปิด" ในตัวจัดการการเชื่อมต่อถ้าไคลเอนต์ถูกสร้างขึ้นทั่วโลก
Wand Maker

1
คำสั่ง tools / linux ใดที่มีประโยชน์ในการดีบักหรือ "แสดงภาพ" พฤติกรรมของ ConnectionManager ภายใต้ประทุน ฉันถามเนื่องจากขณะนี้เรามีปัญหาเกี่ยวกับการเชื่อมต่อใน CLOSE_WAIT และเอฟเฟกต์อื่น ๆ และเรากำลังจัดโครงสร้างเพื่อหาวิธีที่ดีในการดูว่าเกิดอะไรขึ้น
Christoph

@WandMaker ฉันค่อนข้างแน่ใจว่าคุณจะเรียกการปิดระบบเมื่อโปรแกรมใด ๆ ออกหรือเมื่อคุณทำงานบางส่วนเสร็จโดยที่คุณไม่จำเป็นต้องเชื่อมต่อใด ๆ ในบางครั้ง
Nicholas DiPiazza

1
@ คริสตอฟnetstatทำงานได้ดีจริงๆ technet.microsoft.com/en-us/sysinternals/bb897437.aspxด้วย
Nicholas DiPiazza

13

การอ่านเอกสารของฉันคือ HttpConnection ไม่ถือว่าเป็นเธรดที่ปลอดภัยและด้วยเหตุนี้ MultiThreadedHttpConnectionManager จึงจัดเตรียมกลุ่ม HttpConnections ที่ใช้ซ้ำได้คุณมี MultiThreadedHttpConnectionManager เดียวที่ใช้ร่วมกันโดยเธรดทั้งหมดและเริ่มต้นครั้งเดียว ดังนั้นคุณต้องมีการปรับแต่งเล็กน้อยสำหรับตัวเลือก A

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

จากนั้นแต่ละเธรดควรใช้ลำดับสำหรับทุกคำขอรับ conection จากพูลและนำกลับมาใช้ใหม่เมื่อทำงานเสร็จ - การใช้บล็อกในที่สุดก็อาจจะดี นอกจากนี้คุณควรตั้งรหัสสำหรับความเป็นไปได้ที่พูลไม่มีการเชื่อมต่อที่พร้อมใช้งานและประมวลผลข้อยกเว้นการหมดเวลา

HttpConnection connection = null
try {
    connection = connman.getConnectionWithTimeout(
                        HostConfiguration hostConfiguration, long timeout) 
    // work
} catch (/*etc*/) {/*etc*/} finally{
    if ( connection != null )
        connman.releaseConnection(connection);
}

ในขณะที่คุณกำลังใช้กลุ่มการเชื่อมต่อคุณจะไม่ปิดการเชื่อมต่อจริง ๆ ดังนั้นสิ่งนี้ไม่ควรกระทบกับปัญหา TIME_WAIT วิธีนี้ช่วยให้มั่นใจได้ว่าแต่ละเธรดจะไม่ค้างการเชื่อมต่อเป็นเวลานาน สังเกตว่า conman เปิดทิ้งไว้


ไม่ได้ตอบคำถามของฉันอย่างแท้จริงว่าวิธีใด (A หรือ B) ดีกว่า
Cheok Yan Cheng

5

ฉันคิดว่าคุณจะต้องใช้ ThreadSafeClientConnManager

คุณสามารถดูวิธีการทำงานได้ที่นี่: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

หรือAndroidHttpClientที่ใช้ภายใน


1
พี่ครับ ไม่มีแผนที่จะย้ายจาก HttpClient 3.x เป็น 4.x เนื่องจาก 3.x ทำงานได้อย่างไม่มีที่ติในแอปพลิเคชันของฉันมาเกือบ 2 ปีแล้ว :)
Cheok Yan Cheng

9
แน่นอนว่าถ้ามีใครมาที่นี่ Googling เพื่อขอคำตอบ :)
Thomas Ahle

5

ด้วย HttpClient 4.5 คุณสามารถทำได้:

CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();

โปรดทราบว่าสิ่งนี้ใช้ Closeable (สำหรับการปิดตัวจัดการการเชื่อมต่อ)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.