1. พื้นหลัง
โปรโตคอล HTTP เป็นโปรโตคอลไร้สัญชาตินั่นคือแต่ละคำขอเป็นอิสระจากกัน ดังนั้นการใช้งานครั้งแรกคือการร้องขอ HTTP ทุกครั้งจะเปิดการเชื่อมต่อซ็อกเก็ต TCP และการเชื่อมต่อจะถูกปิดหลังจากการโต้ตอบเสร็จสิ้น
โปรโตคอล HTTP เป็นโปรโตคอลเพล็กซ์เต็มรูปแบบดังนั้นจึงต้องใช้การจับมือกันสามครั้งและคลื่นสี่คลื่นในการสร้างและตัดการเชื่อมต่อ เห็นได้ชัดว่าในการออกแบบนี้ทุกครั้งที่ฉันส่งคำขอ HTTP มันใช้ทรัพยากรเพิ่มเติมมากมายเช่นการจัดตั้งและการทำลายการเชื่อมต่อ
ดังนั้นโปรโตคอล HTTP จึงได้รับการพัฒนาและการเชื่อมต่อซ็อกเก็ตมัลติเพล็กซ์จะดำเนินการผ่านวิธีการเชื่อมต่อแบบถาวร
จากภาพคุณจะเห็น:
มีการใช้งานการเชื่อมต่อแบบถาวรสองครั้ง: การเชื่อมต่อแบบต่อเนื่องและต่อเนื่องของ HTTP/1.1 สำหรับ HTTP/1.0+
2. Keep-Alive สำหรับ http/1.0+
ตั้งแต่ปี 1996 เบราว์เซอร์และเซิร์ฟเวอร์ HTTP/1.0 จำนวนมากได้ขยายโปรโตคอลนั่นคือโปรโตคอลส่วนขยาย "เก็บรักษา"
โปรดทราบว่าโปรโตคอลส่วนขยายนี้จะปรากฏเป็นส่วนประกอบของ 1.0 "การเชื่อมต่อแบบต่อเนื่องเชิงทดลอง" ไม่ได้ใช้งาน Keep-Alive อีกต่อไปและไม่ได้อธิบายไว้ในข้อกำหนด HTTP/1.1 ล่าสุด แต่แอปพลิเคชันจำนวนมากยังคงดำเนินต่อไป
ไคลเอนต์ที่ใช้ HTTP/1.0 เพิ่ม "การเชื่อมต่อ: Keep-alive" ไปยังส่วนหัวและขอให้เซิร์ฟเวอร์เปิดการเชื่อมต่อ หากเซิร์ฟเวอร์ยินดีที่จะเปิดการเชื่อมต่อนี้จะรวมส่วนหัวเดียวกันในการตอบกลับ หากการตอบสนองไม่มีส่วนหัว "การเชื่อมต่อ: Keep-Alive" ไคลเอนต์จะคิดว่าเซิร์ฟเวอร์ไม่รองรับการเก็บรักษาและจะปิดการเชื่อมต่อปัจจุบันหลังจากส่งข้อความตอบกลับ
ผ่านโปรโตคอลเสริมแบบ Keep-Alive การเชื่อมต่อแบบถาวรจะเสร็จสมบูรณ์ระหว่างไคลเอนต์และเซิร์ฟเวอร์ แต่ยังมีปัญหาบางอย่าง:
3. การเชื่อมต่อแบบถาวรของ http/1.1
HTTP/1.1 ใช้วิธีการเชื่อมต่อแบบถาวรเพื่อแทนที่ Keep-Alive
การเชื่อมต่อ HTTP/1.1 นั้นคงอยู่โดยค่าเริ่มต้น หากคุณต้องการปิดอย่างชัดเจนคุณต้องเพิ่มการเชื่อมต่อ: ปิดส่วนหัวไปยังข้อความ นั่นคือใน HTTP/1.1 การเชื่อมต่อทั้งหมดมีมัลติเพล็กซ์
อย่างไรก็ตามเช่นเดียวกับการเชื่อมต่อแบบถาวรที่ไม่ได้ใช้งานสามารถปิดได้โดยไคลเอนต์และเซิร์ฟเวอร์ได้ตลอดเวลา การไม่ส่งการเชื่อมต่อ: ปิดไม่ได้หมายความว่าเซิร์ฟเวอร์สัญญาว่าการเชื่อมต่อจะยังคงเปิดอยู่ตลอดไป
4. วิธีการสร้างการเชื่อมต่อแบบถาวรโดย httpClient
HttpClien ใช้พูลการเชื่อมต่อเพื่อจัดการการเชื่อมต่อการถือครอง ในลิงค์ TCP เดียวกันการเชื่อมต่อสามารถนำกลับมาใช้ใหม่ได้ httpClient เชื่อมต่อการคงอยู่ผ่านการรวมการเชื่อมต่อ
ในความเป็นจริงเทคโนโลยี "พูล" เป็นการออกแบบทั่วไปและแนวคิดการออกแบบไม่ซับซ้อน:
กลุ่มการเชื่อมต่อทั้งหมดมีความคิดนี้ แต่เมื่อเราดูที่ซอร์สโค้ด httpClient เราจะมุ่งเน้นไปที่สองจุด:
4.1 การใช้งานกลุ่มการเชื่อมต่อ httpClient
การประมวลผลการเชื่อมต่อแบบถาวรของ HttpClient สามารถสะท้อนได้ในรหัสต่อไปนี้ ต่อไปนี้คือการแยกชิ้นส่วนที่เกี่ยวข้องกับพูลเชื่อมต่อจาก MainClientExec และลบส่วนอื่น ๆ :
คลาสสาธารณะ MainClientExec ดำเนินการ clientExecchain {@Override Public CloseableHttPresponse ดำเนินการ (เส้นทางสุดท้าย httproute, การร้องขอ httprequestwrapper สุดท้าย, การเชื่อมต่อ httpClientCextion ครั้งสุดท้าย Final ConnectionRequest ConnRequest = ConnManager.RequestConnection (เส้นทาง, usertoken); httpClientConnection สุดท้าย ManagedConn; Final Int Timeout = config.getConnectionRequestTimeout (); // รับการเชื่อมต่อที่ได้รับการจัดการจากการเชื่อมต่อการเชื่อมต่อการเชื่อมต่อการเชื่อมต่อ questhttpClientConnection managedConn = connrequest.get (หมดเวลา> 0? หมดเวลา: 0, timeUnit.milliseconds); // ส่งผู้จัดการการเชื่อมต่อ httpClientConnectionManager และการเชื่อมต่อที่มีการจัดการ httpClientConnection ไปยังผู้เชื่อมต่อถือ Connectionholder Connolholder = NEW ConnectionHolder (this.log, this.ConnManager, ManagedConn); ลอง {httpresponse response; if (! managedConn.isopen ()) {// หากการเชื่อมต่อที่มีการจัดการในปัจจุบันไม่ได้อยู่ในสถานะเปิดคุณจะต้องสร้างการเชื่อมต่อที่จัดตั้งขึ้นอีกครั้ง (Proxyauthstate, ManagedConn, เส้นทาง, คำขอ, บริบท); } // ส่งคำขอผ่านการเชื่อมต่อ httpClientConnection response = requestExecutor.execute (คำขอ, ManagedConn, บริบท); // แยกแยะว่าการเชื่อมต่อสามารถนำกลับมาใช้ใหม่ผ่านกลยุทธ์การใช้การเชื่อมต่อซ้ำได้หรือไม่หาก (reuseStrateGy.keepalive (การตอบสนอง, บริบท)) {// รับช่วงเวลาการเชื่อมต่อความถูกต้องระยะเวลาสุดท้าย = Keepalivestrategy.GetKeepAliveduration (การตอบสนองบริบท); // ตั้งค่าระยะเวลาการเชื่อมต่อความถูกต้อง Connholder.SetValidfor (ระยะเวลา, TimeUnit.milliseconds); // ทำเครื่องหมายการเชื่อมต่อปัจจุบันเป็นรัฐที่นำกลับมาใช้ใหม่ได้. markreusable (); } else {connholder.marknonreusable (); }} entity httpentity สุดท้าย = response.getEntity (); if (entity == null ||! entity.issTreaming ()) {// ปล่อยการเชื่อมต่อปัจจุบันไปยังพูลสำหรับการโทรครั้งต่อไปไปที่ Connholder.ReleAseconnection (); ส่งคืน httpresponseproxy ใหม่ (การตอบสนอง, null); } else {ส่งคืน httpresponseproxy ใหม่ (การตอบสนอง, ผู้เชื่อมต่อ); -ที่นี่เราเห็นว่าการประมวลผลการเชื่อมต่อระหว่างกระบวนการร้องขอ HTTP สอดคล้องกับข้อกำหนดของโปรโตคอล ที่นี่เราจะหารือเกี่ยวกับการใช้งานเฉพาะ
PoolinghttpClientConnectionManager เป็นตัวจัดการการเชื่อมต่อเริ่มต้นของ httpClient ขั้นแรกให้ขอรับคำขอการเชื่อมต่อผ่าน RequestConnection () โปรดทราบว่านี่ไม่ใช่การเชื่อมต่อ
Public ConnectionRequest RequestConnection (เส้นทางสุดท้าย httproute, สถานะวัตถุสุดท้าย) {อนาคตสุดท้าย <cpoolentry> future = this.pool.lease (เส้นทาง, รัฐ, null); ส่งคืน ConnectionRequest ใหม่ () {@Override บูลีนสาธารณะยกเลิก () {return future.cancel (จริง); } @Override public httpClientConnection รับ (การหมดเวลาครั้งสุดท้าย, timunit tunit ครั้งสุดท้าย) พ่น InterruptedException, ExecutionException, ConnectPoolTimeOutException {สุดท้าย httpClientConnection Conn = Leaseconnection if (conn.isopen ()) {โฮสต์ httphost สุดท้าย; if (route.getProxyHost ()! = null) {host = route.getProxyHost (); } else {host = route.getTargetHost (); } ขั้นสุดท้าย socketConfig socketConfig = resolvesocketConfig (โฮสต์); conn.setsockettimeout (SocketConfig.getSotimeout ()); } return conn; - -คุณจะเห็นได้ว่าวัตถุ ConnectionRequest ที่ส่งคืนนั้นเป็นอินสแตนซ์การเชื่อมต่อจริงที่เก็บอนาคต <CpoolEntry> ซึ่งจัดการโดยกลุ่มการเชื่อมต่อ
จากรหัสข้างต้นเราควรมุ่งเน้น:
Future<CPoolEntry> future = this.pool.lease(route, state, null)
วิธีรับการเชื่อมต่อแบบอะซิงโครนัสจาก cpool การเชื่อมต่อในอนาคต <cpoolentry>
HttpClientConnection conn = leaseConnection(future, timeout, tunit)
วิธีรับการเชื่อมต่อที่แท้จริงโดยการเชื่อมต่อแบบอะซิงโครนัสกับอนาคต <CpoolEntry>
4.2 อนาคต <CpoolEntry>
ลองมาดูกันว่า cpool เปิดตัวอนาคต <CpoolEntry> ในอนาคตได้อย่างไร รหัสหลักของ AbstractConnpool มีดังนี้:
Private E GetPoolEntryBlocking (เส้นทางสุดท้าย T, Final Object State, Final Long Timeout, Final TimeUnit Tunit, Final Future <E> อนาคต) โยน iOexception, InterruptedException, TimeOutException {// ครั้งแรกล็อคกลุ่มการเชื่อมต่อปัจจุบัน ล็อคปัจจุบันคือ reentrantlockthis.lock.lock (); ลอง {// รับพูลการเชื่อมต่อที่สอดคล้องกับ httproute ปัจจุบัน สำหรับกลุ่มการเชื่อมต่อของ httpClient สระรวมทั้งหมดมีขนาดและการเชื่อมต่อที่สอดคล้องกับแต่ละเส้นทางก็เป็นสระว่ายน้ำดังนั้นจึงเป็น "พูลในสระว่ายน้ำ" เส้นทางสุดท้ายเส้นทางสุดท้าย <t, c, e> pool = getPool (เส้นทาง); รายการ E; สำหรับ (;;) {asserts.check (! this.isshutdown, "การเชื่อมต่อพูลปิด"); // รับการเชื่อมต่อจากพูลที่สอดคล้องกับเส้นทางซึ่งอาจเป็นโมฆะหรือรายการเชื่อมต่อที่ถูกต้อง = pool.getFree (สถานะ); // ถ้าคุณได้รับ null ออกจากลูปถ้า (รายการ == null) {break; } // หากคุณได้รับการเชื่อมต่อที่หมดอายุหรือการเชื่อมต่อถูกปิดให้ปล่อยทรัพยากรและดำเนินการต่อเพื่อรับหาก (entry.isexpired (System.currentTimeLis ()))) {entry.close (); } if (entry.isclosed ()) {this.available.remove (รายการ); pool.free (รายการ, เท็จ); } else {// หากคุณได้รับการเชื่อมต่อที่ถูกต้องให้ออกจากการแบ่งลูป; }} // หากคุณได้รับการเชื่อมต่อที่ถูกต้องให้ออกจาก (รายการ! = null) {this.available.remove (รายการ); this.leased.add (รายการ); onreuse (รายการ); รายการกลับ; } // ไปที่นี่พิสูจน์ว่าไม่ได้รับการเชื่อมต่อที่ถูกต้องคุณต้องสร้าง int maxperRoute สุดท้าย = getMax (เส้นทาง); // จำนวนการเชื่อมต่อสูงสุดที่สอดคล้องกับแต่ละเส้นทางสามารถกำหนดค่าได้ หากเกินกว่าคุณจะต้องทำความสะอาดการเชื่อมต่อบางอย่างผ่าน LRU Final Int ส่วนเกิน = Math.Max (0, Pool.GetAllocatedCount () + 1 - MaxperRoute); ถ้า (ส่วนเกิน> 0) {สำหรับ (int i = 0; i <ส่วนเกิน; i ++) {สุดท้าย e สุดท้าย = pool.getLastused (); if (lastused == null) {break; } lastused.close (); this.available.remove (สุดท้าย); Pool.remove (สุดท้าย); }} // จำนวนการเชื่อมต่อในพูลเส้นทางปัจจุบันยังไม่ถึงออนไลน์ถ้า (pool.getAllocatedCount () <maxperRoute) {สุดท้าย int totalused = this.leased.size (); int freeCapacity สุดท้าย = math.max (this.maxtotal - totalused, 0); // ตัดสินว่ากลุ่มการเชื่อมต่อเกินบรรทัดออนไลน์หรือไม่ หากเกินกว่านั้นคุณจะต้องทำความสะอาดการเชื่อมต่อบางอย่างผ่าน LRU ถ้า (freeCapacity> 0) {สุดท้าย int totalAvailable = this.available.size (); // หากจำนวนการเชื่อมต่อฟรีมากกว่าพื้นที่ว่างที่เหลืออยู่แล้วคุณจะต้องทำความสะอาดการเชื่อมต่อฟรีถ้า (totalavailable> freecapacity - 1) {ถ้า (! this.available.isempty ()) endused.close (); RoutespecificPool สุดท้าย <t, C, E> otherPool = getPool (lastused.getRoute ()); otherpool.remove (สุดท้าย); }} // สร้างการเชื่อมต่อตามเส้นทางสุดท้าย C Conn = this.connfactory.create (เส้นทาง); // ใส่การเชื่อมต่อนี้ลงใน "พูลขนาดเล็ก" ที่สอดคล้องกับเส้นทางการเข้าสู่เส้นทาง = pool.add (conn); // ใส่การเชื่อมต่อนี้ลงใน "พูลขนาดใหญ่" this.leased.add (รายการ); รายการกลับ; }} // ถึงจุดสิ้นสุดนี้ได้รับการพิสูจน์แล้วว่าไม่มีการเชื่อมต่อที่ถูกต้องจากพูลเส้นทางที่ได้รับและเมื่อคุณต้องการสร้างการเชื่อมต่อด้วยตัวเองพูลการเชื่อมต่อเส้นทางปัจจุบันได้ถึงค่าสูงสุดนั่นคือมีการเชื่อมต่อที่ใช้งานอยู่แล้ว ลอง {ถ้า (future.iscancelled ()) {โยน InterruptedException ใหม่ ("การดำเนินการขัดจังหวะ"); } // ใส่อนาคตลงในสระว่ายน้ำเส้นทางรอพูล. คิว (อนาคต); // นำอนาคตเข้าสู่กลุ่มการเชื่อมต่อขนาดใหญ่ที่รอสิ่งนี้. pending.add (อนาคต); // ถ้าคุณรอการแจ้งเตือนของเซมาฟอร์ความสำเร็จจะเป็นจริงถ้า (กำหนดเวลา! = null) {success = this.condition.awaituntil (กำหนดเวลา); } else {this.condition.await (); ความสำเร็จ = จริง; } if (future.iscelled ()) {โยน InterruptedException ใหม่ ("การดำเนินการขัดจังหวะ"); }} ในที่สุด {// ลบ pool.unqueue (อนาคต); this.pending.remove (อนาคต); } // หากการแจ้งเตือนสัญญาณไม่มีการรอและเวลาปัจจุบันได้หมดเวลาลูปจะถูกออกหาก (! ความสำเร็จ && (กำหนดเวลา! }} // ในที่สุดก็ไม่ได้รับการแจ้งเตือนสัญญาณสัญญาณและไม่ได้รับการเชื่อมต่อที่มีอยู่ โยน TimeOutException ใหม่ ("การรอหมดเวลารอการเชื่อมต่อ"); } ในที่สุด {// ปล่อยล็อคบนพูลการเชื่อมต่อขนาดใหญ่ this.lock.unlock (); -มีจุดสำคัญหลายประการในตรรกะรหัสข้างต้น:
จนถึงตอนนี้โปรแกรมได้รับอินสแตนซ์ cpoolentry ที่มีอยู่หรือยกเลิกโปรแกรมโดยการโยนข้อยกเว้น
4.3 httpClientConnection
ได้รับการป้องกัน httpClientConnection leaseconnection (อนาคตสุดท้าย <CpoolEntry> อนาคตการหมดเวลาครั้งสุดท้าย, timunit tunit ครั้งสุดท้าย) โยน interruptedException, ExecutionException, ConnectPoolTimeOutException {รายการ cpoolentry สุดท้าย; ลอง {// รับรายการ cpoolentry จากการดำเนินการแบบอะซิงโครนัสในอนาคต <CpoolEntry> รายการ = future.get (หมดเวลา, tunit); if (entry == null || future.iscelled ()) {โยน interruptedException ใหม่ (); } asserts.check (entry.getConnection ()! = null, "รายการพูลที่ไม่มีการเชื่อมต่อ"); if (this.log.isdebugenabled ()) {this.log.debug ("การเชื่อมต่อ Invented:" + รูปแบบ (รายการ) + formatStats (entry.getRoute ())); } // รับวัตถุพร็อกซีของ cpoolentry และการดำเนินการทั้งหมดจะทำโดยใช้ httpClientConnection return cpoolproxy.newproxy (รายการ); } catch (Final TimeOutException ex) {โยน connectionPoolTimeOutException ใหม่ ("การหมดเวลารอการเชื่อมต่อจากพูล"); - 5. วิธีนำการเชื่อมต่อแบบถาวรกลับมาใช้ใหม่ใน httpClient ได้อย่างไร?
ในบทก่อนหน้าเราเห็นว่า httpClient ได้รับการเชื่อมต่อผ่านกลุ่มการเชื่อมต่อและรับพวกเขาจากสระว่ายน้ำเมื่อจำเป็นต้องใช้การเชื่อมต่อ
สอดคล้องกับบทที่สาม:
ในบทที่ 4 เราเห็นว่า httpclient จัดการกับปัญหาของ 1 และ 3 ได้อย่างไรเราจะจัดการกับคำถามที่สองได้อย่างไร?
นั่นคือ HTTPClient จะพิจารณาว่าควรปิดการเชื่อมต่อหลังการใช้งานอย่างไรหรือควรวางไว้ในกลุ่มเพื่อให้ผู้อื่นนำมาใช้ซ้ำได้อย่างไร ดูรหัสของ MainClientExec
// ส่งการตอบสนองการเชื่อมต่อ http = requestexecutor.execute (คำขอ, managedconn, บริบท); // ปกป้องว่าการเชื่อมต่อในปัจจุบันจะถูกนำมาใช้ซ้ำตามกลยุทธ์การใช้ซ้ำหรือไม่ถ้า (reuseStrateGy.keepalive (การตอบสนองบริบท)) {// การเชื่อมต่อที่ต้องนำกลับมาใช้ใหม่รับเวลาการเชื่อมต่อเวลาการเชื่อมต่อตามการหมดเวลาในการตอบสนองระยะยาวเป็นระยะเวลานาน = if (this.log.isdebugenabled ()) {สตริงสุดท้าย s; // หมดเวลาคือจำนวนมิลลิวินาทีถ้าไม่ได้ตั้งค่าคือ -1 นั่นคือไม่มีการหมดเวลาถ้า (ระยะเวลา> 0) {s = "สำหรับ" + ระยะเวลา + "" + timeunit.milliseconds; } else {s = "ไม่มีกำหนด"; } this.log.debug ("การเชื่อมต่อสามารถรักษาชีวิตได้" + s); } // ตั้งเวลาหมดเวลา เมื่อคำขอสิ้นสุดลงผู้จัดการการเชื่อมต่อจะตัดสินใจว่าจะปิดหรือนำกลับเข้าไปในสระว่ายน้ำตามเวลาหมดเวลา Connholder.setValidfor (ระยะเวลา, TimeUnit.milliseconds); // ลงทะเบียนการเชื่อมต่อเป็นผู้เชื่อมต่อที่ใช้ซ้ำได้ markreusable (); } else {// ลงทะเบียนการเชื่อมต่อเป็น Connholder.markNonreusable (); -จะเห็นได้ว่าหลังจากคำขอเกิดขึ้นโดยใช้การเชื่อมต่อมีนโยบายการเชื่อมต่อใหม่เพื่อตัดสินใจว่าจะนำการเชื่อมต่อกลับมาใช้ใหม่หรือไม่ หากมีการนำกลับมาใช้ใหม่มันจะถูกส่งไปยัง httpClientConnectionManager หลังจากสิ้นสุด
ดังนั้นตรรกะของนโยบายมัลติเพล็กซ์การเชื่อมต่อคืออะไร?
คลาสสาธารณะ defaultClientConnectionReusEstrategy ขยาย defaultConnectionReusEStrategy {สาธารณะคงที่ public defaultClientConnectionReusEsTrategy อินสแตนซ์ = ใหม่ defaultClientConnectionReusEsTrategy (); @Override Public Boolean Keepalive (การตอบกลับ httpresponse สุดท้าย, บริบท httpcontext สุดท้าย) {// รับคำขอสุดท้าย httprequest สุดท้ายจากบริบทสุดท้ายคำขอ httprequest = (httprequest) บริบท. getattribute (httpcorecontext.http_request); if (คำขอ! = null) {// รับส่วนหัวส่วนหัวของการเชื่อมต่อสุดท้าย [] connheaders = request.getheaders (httpheaders.connection); if (connheaders.length! = 0) {สุดท้าย tokeniterator ti = ใหม่ basictokeniterator (basicheaderiterator ใหม่ (Connheaders, null)); ในขณะที่ (ti.hasnext ()) {โทเค็นสตริงสุดท้าย = ti.nexttoken (); // หากการเชื่อมต่อ: ปิดส่วนหัวปิดอยู่นั่นหมายความว่าคำขอไม่ได้ตั้งใจที่จะเก็บการเชื่อมต่อและความตั้งใจของการตอบกลับจะถูกละเว้น ส่วนหัวนี้เป็นข้อกำหนดของ http/1.1 ถ้า (http.conn_close.equalsignorecase (โทเค็น)) {return false; }}}}} // ใช้กลยุทธ์การใช้ซ้ำของคลาสแม่เพื่อส่งคืน super.keepalive (การตอบสนองบริบท); -ดูกลยุทธ์การใช้ซ้ำของชั้นเรียนผู้ปกครอง
if (canResponseHaveBody (คำขอ, การตอบกลับ)) {ส่วนหัวสุดท้าย [] clhs = response.getheaders (http.content_len); // หากความยาวเนื้อหาของการตอบสนองไม่ถูกตั้งค่าอย่างถูกต้องการเชื่อมต่อจะไม่ถูกนำกลับมาใช้ใหม่ // เนื่องจากสำหรับการเชื่อมต่อแบบถาวรไม่จำเป็นต้องสร้างการเชื่อมต่อระหว่างการส่งสัญญาณทั้งสองอีกครั้งคุณต้องยืนยันว่าคำขอใดเป็นของความยาวเนื้อหา CLHS [0]; ลอง {final int contentLen = integer.parseint (clh.getValue ()); if (contentLen <0) {return false; }} catch (สุดท้าย numberFormatexception ex) {return false; }} else {return false; }} if (headerIterator.hasnext ()) {ลอง {สุดท้าย tokeniterator ti = basictokeniterator ใหม่ (headerIterator); บูลีน keepalive = false; ในขณะที่ (ti.hasnext ()) {โทเค็นสตริงสุดท้าย = ti.nexttoken (); // หากการตอบกลับมีการเชื่อมต่อ: ปิดส่วนหัวจะมีการระบุอย่างชัดเจนว่าจะต้องปิดและถ้า (http.conn_close.equalsignorecase (โทเค็น)) {return false; // หากการตอบสนองมีการเชื่อมต่อ: ส่วนหัวที่เก็บรักษาไว้จะมีการระบุไว้อย่างชัดเจนว่าจะต้องคงอยู่มันจะถูกนำกลับมาใช้ใหม่อีกครั้งถ้า (http.conn_keep_alive.equalsignorecase (โทเค็น)) {keepalive = true; }} if (keepalive) {return true; }} catch (สุดท้าย parseexception px) {return false; }}} // หากไม่มีคำอธิบายส่วนหัวการเชื่อมต่อที่เกี่ยวข้องในการตอบสนองการเชื่อมต่อทั้งหมดที่สูงกว่ารุ่น HTTP/1.0 จะถูกนำกลับมาใช้ใหม่เพื่อกลับมาอีกครั้ง! ver.lessequals (httpversion.http_1_0);เพื่อสรุป:
ดังที่เห็นได้จากรหัสกลยุทธ์การใช้งานของมันสอดคล้องกับข้อ จำกัด ของบทที่ 2 และบทที่ 3 ชั้นโปรโตคอล
6. วิธีทำความสะอาดการเชื่อมต่อที่หมดอายุจาก httpClient
ก่อนที่ HTTPClient 4.4 เมื่อนำการเชื่อมต่อกลับมาใช้ใหม่จากกลุ่มการเชื่อมต่อจะตรวจสอบว่ามันจะหมดอายุหรือไม่และทำความสะอาดหากหมดอายุ
เวอร์ชันที่ตามมาจะแตกต่างกัน จะมีเธรดแยกต่างหากเพื่อสแกนการเชื่อมต่อในพูลเชื่อมต่อ หลังจากพบว่ามีการใช้งานครั้งสุดท้ายของเวลาที่ตั้งไว้แล้วมันจะถูกทำความสะอาด การหมดเวลาเริ่มต้นคือ 2 วินาที
public closeablehttpClient build () {// ถ้าคุณระบุว่าคุณต้องการทำความสะอาดการเชื่อมต่อที่หมดอายุและไม่ได้ใช้งานเธรดการทำความสะอาดจะเริ่มขึ้น ค่าเริ่มต้นไม่ได้เริ่มถ้า (evictexpiredConnections || evictidleConnections) {// สร้างเธรดที่สะอาดสำหรับพูลการเชื่อมต่อสุดท้าย IdleConnectionEvictor ConnectionEvictor = IdleConnectionEvictor ใหม่ (CM, Maxidletime> 0? MaxidLetime, MaxidleTimeUnit); closeablescopy.add (ใหม่ปิด () {@Override public void close () พ่น ioexception {connectionevictor.shutdown (); ลอง {connectionevictor.awaittermination (1l, timeunit.seconds); // ดำเนินการทำความสะอาดเธรด connectionEvictor.start ();}คุณจะเห็นว่าเมื่อมีการระบุ httpClientBuilder หากมีการระบุฟังก์ชั่นการทำความสะอาดเธรดการทำความสะอาดพูลการเชื่อมต่อจะถูกสร้างและเรียกใช้
IdleConnectionEvictor สาธารณะ (สุดท้าย httpClientConnectionManager connectionManager สุดท้าย ThreadFactory ThreadFactory สุดท้ายเวลานอนระยะยาวสุดท้ายเวลา sleeptimeUnit สุดท้าย, maxidletime สุดท้าย, maxidletimeunit สุดท้าย) this.threadFactory = ThreadFactory! = null? ThreadFactory: New DefaultThreadFactory (); this.sleeptimems = sleeptimeUnit! = null? SleeptimeUnit.tomillis (เวลานอน): เวลานอน; this.maxidletimems = maxidletimeunit! = null? maxidletimeunit.tomillis (maxidletime): maxidletime; this.thread = this.threadFactory.newThread (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {ลอง {// วนวนที่ตายแล้วเธรดจะดำเนินการในขณะที่ (! thead.currentthread () (); connectionManager.closeexpiredConnections (); -เพื่อสรุป:
7. สรุปบทความนี้
การวิจัยข้างต้นขึ้นอยู่กับความเข้าใจส่วนบุคคลของซอร์สโค้ด HTTPClient หากมีข้อผิดพลาดใด ๆ ฉันหวังว่าทุกคนจะฝากข้อความไว้เพื่อพูดคุยกันอย่างแข็งขัน
โอเคข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com