ניהול חיבורי HttpClient

1. סקירה כללית

במאמר זה נעבור על יסודות ניהול החיבורים בתוך HttpClient 4.

נסקור את השימוש ב- BasichttpClientConnectionManager ו PoolingHttpClientConnectionManager לאכוף שימוש בטוח, תואם פרוטוקול ויעיל בחיבורי HTTP.

2. ה BasicHttpClientConnectionManager לחיבור הברגה אחת ברמה נמוכה

ה BasicHttpClientConnectionManager זמין מאז HttpClient 4.3.3 כהטמעה הפשוטה ביותר של מנהל חיבורי HTTP. הוא משמש ליצירה וניהול של חיבור יחיד שיכול לשמש רק חוט אחד בכל פעם.

דוגמא 2.1. קבלת בקשת חיבור לחיבור ברמה נמוכה (HttpClientConnection)

BasicHttpClientConnectionManager connManager = BasicHttpClientConnectionManager חדש (); מסלול HttpRoute = HttpRoute חדש (HttpHost חדש ("www.baeldung.com", 80)); ConnectionRequest connRequest = connManager.requestConnection (מסלול, null);

ה בקשה חיבור השיטה מקבלת מהמנהל מאגר חיבורים עבור ספציפי מַסלוּל להתחבר. ה מַסלוּל הפרמטר מציין מסלול של "כשות פרוקסי" למארח היעד, או למארח היעד עצמו.

ניתן לבצע בקשה באמצעות HttpClientConnection באופן ישיר, אך יש לזכור גישה ברמה נמוכה זו היא מילולית וקשה לניהול. חיבורים ברמה נמוכה שימושיים לגישה לשקעים ולנתוני חיבורים כגון פסק זמן ומידע מארח יעד, אך לצורך ביצוע רגיל, HttpClient הוא ממשק API הרבה יותר קל לעבוד נגדו.

3. באמצעות PoolingHttpClientConnectionManager להשיג ולנהל מאגר חיבורים מרובי-הברגה

ה PoolingHttpClientConnectionManager תיצור ותנהל מאגר חיבורים לכל מסלול או יעד מארח בו אנו משתמשים. גודל ברירת המחדל של מאגר המקביל חיבורים שיכול להיות פתוח על ידי המנהל הוא 2 לכל מסלול או מארח יעד, ו 20 לסך הכל חיבורים פתוחים. ראשית - בואו נסתכל כיצד להגדיר את מנהל החיבורים הזה ב- HttpClient פשוט:

דוגמא 3.1. הגדרת PoolingHttpClientConnectionManager על HttpClient

HttpClientConnectionManager poolingConnManager = Pooling חדש HttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). SetConnectionManager (poolingConnManager) .build (); client.execute (HttpGet חדש ("/")); assertTrue (poolingConnManager.getTotalStats (). getLeased () == 1);

לאחר מכן - בואו נראה כיצד ניתן להשתמש באותו מנהל חיבורים על ידי שני HttpClients הפועלים בשני שרשורים שונים:

דוגמא 3.2. שימוש בשני HttpClients להתחברות למארח יעד אחד כל אחד

HttpGet get1 = HttpGet חדש ("/"); HttpGet get2 = HttpGet חדש ("// google.com"); PoolingHttpClientConnectionManager connManager = חדש PoolingHttpClientConnectionManager (); CloseableHttpClient client1 = HttpClients.custom (). SetConnectionManager (connManager) .build (); CloseableHttpClient client2 = HttpClients.custom (). SetConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = MultiHttpClientConnThread חדש (client1, get1); MultiHttpClientConnThread thread2 = MultiHttpClientConnThread חדש (client2, get2); thread1.start (); thread2.start (); thread1.join (); thread2.join ();

שימו לב שאנחנו משתמשים יישום פשוט מאוד של חוטים מותאמים אישית - הנה זה:

דוגמא 3.3. חוט מותאם אישית ביצוע א קבל בקשה

מחלקה ציבורית MultiHttpClientConnThread מרחיב אשכול {לקוח פרטי CloseableHttpClient; פרטי HttpGet get; // קונסטרוקציות סטנדרטיות מבניות ריקות () {נסה {HttpResponse תגובה = client.execute (get); EntityUtils.consume (response.getEntity ()); } לתפוס (ClientProtocolException לשעבר) {} לתפוס (לשעבר IOException) {}}}

שימו לב לEntityUtils.consume (response.getEntity) שיחה - הכרחי לצרוך את כל תוכן התגובה (ישות) כדי שהמנהל יוכל שחרר את החיבור חזרה לבריכה.

4. הגדר את תצורת מנהל החיבורים

ברירות המחדל של מנהל חיבורי הבריכה נבחרו היטב אך - בהתאם למקרה השימוש שלך - עשויות להיות קטנות מדי. אז - בואו נסתכל כיצד נוכל להגדיר:

  • המספר הכולל של חיבורים
  • המספר המרבי של חיבורים לכל מסלול (כלשהו)
  • המספר המרבי של חיבורים למסלול ספציפי יחיד

דוגמה 4.1. הגדלת מספר החיבורים שניתן לפתוח ולנהל מעבר למגבלות ברירת המחדל

PoolingHttpClientConnectionManager connManager = PoolingHttpClientConnectionManager חדש (); connManager.setMaxTotal (5); connManager.setDefaultMaxPerRoute (4); מארח HttpHost = HttpHost חדש ("www.baeldung.com", 80); connManager.setMaxPerRoute (HttpRoute חדש (מארח), 5);

בואו נסכם את ה- API:

  • setMaxTotal (int מקסימום): הגדר את המספר המרבי של סך החיבורים הפתוחים.
  • setDefaultMaxPerRoute (מקסימום int): הגדר את המספר המרבי של חיבורים מקבילים לכל מסלול, שהוא 2 כברירת מחדל.
  • setMaxPerRoute (מקסימום int): הגדר את המספר הכולל של חיבורים מקבילים למסלול ספציפי, שהוא 2 כברירת מחדל.

אז, מבלי לשנות את ברירת המחדל, אנחנו הולכים להגיע לגבולות מנהל החיבורים די בקלות - בואו נראה איך זה נראה:

דוגמה 4.2. שימוש בחוטים לביצוע חיבורים

HttpGet get = HttpGet new ("// www.baeldung.com"); PoolingHttpClientConnectionManager connManager = חדש PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). setConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = MultiHttpClientConnThread חדש (לקוח, קבל); MultiHttpClientConnThread thread2 = MultiHttpClientConnThread חדש (לקוח, קבל); MultiHttpClientConnThread thread3 = MultiHttpClientConnThread חדש (לקוח, קבל); thread1.start (); thread2.start (); thread3.start (); thread1.join (); thread2.join (); thread3.join ();

כפי שכבר דנו, מגבלת החיבור למארח היא 2 כברירת מחדל. לכן, בדוגמה זו, אנו מנסים להכין 3 פתילים 3 בקשות לאותו מארח, אך רק 2 חיבורים יוקצו במקביל.

בואו נסתכל על היומנים - יש לנו שלושה שרשורים פועלים אבל רק 2 חיבורים מושכרים:

[Thread-0] INFO obhcMultiHttpClientConnThread - לפני - חיבורים מושכרים = 0 [Thread-1] INFO obhcMultiHttpClientConnThread - Before - Leased Connections = 0 [Thread-2] INFO obhcMultiHttpClientConnThread - לפני - חיבורים מושכרים = 0 INFO obhcMultiHttpClientConnThread - אחרי - חיבורים מושכרים = 2 [Thread-0] INFO obhcMultiHttpClientConnThread - אחרי - חיבורים מושכרים = 2

5. אסטרטגיית Keep-Alive Connection

הצעת מחיר ל- HttpClient 4.3.3. התייחסות: "אם ה להשאיר בחיים הכותרת אינה קיימת בתגובה, HttpClient מניח שהקשר יכול להישאר בחיים ללא הגבלת זמן. " (ראה הפניה ל- HttpClient).

כדי לעקוף את זה, ולהצליח לנהל חיבורים מתים אנו זקוקים ליישום אסטרטגיה מותאם אישית ולבנות אותה לתוך ה- HttpClient.

דוגמה 5.1. אסטרטגיה של Keep Keep Alive בהתאמה אישית

ConnectionKeepAliveStrategy myStrategy = חדש ConnectionKeepAliveStrategy () {@Override ציבורי ארוך getKeepAliveDuration (HttpResponse תגובה, HttpContext הקשר) {HeaderElementIterator זה = חדש BasicHeaderElementIterator (תגובה.headerIterator) בעוד (it.hasNext ()) {HeaderElement הוא = it.nextElement (); מחרוזת param = he.getName (); ערך מחרוזת = he.getValue (); אם (value! = null && param.equalsIgnoreCase ("פסק זמן")) {return Long.parseLong (value) * 1000; }} להחזיר 5 * 1000; }};

אסטרטגיה זו תנסה תחילה ליישם את המארח להשאיר בחיים המדיניות כאמור בכותרת. אם מידע זה אינו קיים בכותרת התגובה הוא ישמור על קשרים בחיים למשך 5 שניות.

עכשיו - בואו ניצור לקוח עם אסטרטגיה מותאמת אישית זו:

PoolingHttpClientConnectionManager connManager = חדש PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom () .setKeepAliveStrategy (myStrategy) .setConnectionManager (connManager) .build ();

6. התמדה / שימוש חוזר בחיבור

המפרט HTTP / 1.1 קובע כי ניתן להשתמש מחדש בחיבורים אם לא נסגרו - זה מכונה התמדה של חיבור.

ברגע ששוחרר חיבור על ידי המנהל הוא נשאר פתוח לשימוש חוזר. בעת שימוש ב- BasicHttpClientConnectionManager, שיכול רק למלא חיבור אחד, יש לשחרר את החיבור לפני שהוא מושכר שוב:

דוגמא 6.1. BasicHttpClientConnectionManagerשימוש חוזר בחיבור

BasicHttpClientConnectionManager basicConnManager = חדש BasicHttpClientConnectionManager (); HttpClientContext context = HttpClientContext.create (); // מסלול HttpRoute ברמה נמוכה = HttpRoute חדש (HttpHost חדש ("www.baeldung.com", 80)); ConnectionRequest connRequest = basicConnManager.requestConnection (מסלול, null); HttpClientConnection conn = connRequest.get (10, TimeUnit.SECONDS); basicConnManager.connect (חיבור, מסלול, 1000, הקשר); basicConnManager.routeComplete (חיבור, מסלול, הקשר); HttpRequestExecutor exeRequest = HttpRequestExecutor חדש (); context.setTargetHost ((HttpHost חדש ("www.baeldung.com", 80))); HttpGet get = HttpGet new ("// www.baeldung.com"); exeRequest.execute (קבל, קבל, הקשר); basicConnManager.releaseConnection (conn, null, 1, TimeUnit.SECONDS); // CloseableHttpClient client high level = HttpClients.custom () .setConnectionManager (basicConnManager) .build (); client.execute (קבל);

בואו נסתכל על מה שקורה.

ראשית - שימו לב שאנחנו משתמשים קודם בחיבור ברמה נמוכה, רק כדי שיהיה לנו שליטה מלאה מתי יוצא החיבור, ואז חיבור רגיל ברמה גבוהה יותר עם HttpClient. ההיגיון המורכב ברמה נמוכה לא מאוד רלוונטי כאן - הדבר היחיד שאכפת לנו ממנו הוא releaseConnection שִׂיחָה. זה משחרר את החיבור היחיד הזמין ומאפשר להשתמש בו מחדש.

לאחר מכן, הלקוח מבצע את בקשת ה- GET שוב בהצלחה. אם נדלג על שחרור החיבור, נקבל IllegalStateException מ- HttpClient:

java.lang.IllegalStateException: החיבור עדיין מוקצה ב- o.a.h.u.Asserts.check (Asserts.java:34) ב- o.a.h.i.c.BasicHttpClientConnectionManager.getConnection (BasicHttpClientConnectionManager.java:248)

שים לב שהחיבור הקיים אינו סגור, פשוט שוחרר ואז נעשה בו שימוש חוזר על ידי הבקשה השנייה.

בניגוד לדוגמא לעיל, PoolingHttpClientConnectionManager מאפשר שימוש חוזר בחיבור באופן שקוף ללא צורך בשחרור חיבור באופן מרומז:

דוגמא 6.2.PoolingHttpClientConnectionManager: שימוש חוזר בחיבורים עם חוטים

HttpGet get = HttpGet new ("// echo.200please.com"); PoolingHttpClientConnectionManager connManager = חדש PoolingHttpClientConnectionManager (); connManager.setDefaultMaxPerRoute (5); connManager.setMaxTotal (5); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); MultiHttpClientConnThread [] אשכולות = MultiHttpClientConnThread חדש [10]; עבור (int i = 0; i <threads.length; i ++) {threads [i] = MultiHttpClientConnThread חדש (לקוח, get, connManager); } עבור (MultiHttpClientConnThread thread: threads) {thread.start (); } עבור (MultiHttpClientConnThread thread: threads) {thread.join (1000); }

בדוגמה שלמעלה יש 10 אשכולות, שמבצעות 10 בקשות אך רק משתפות 5 חיבורים.

כמובן, דוגמה זו נשענת על השרת להשאיר בחיים פסק זמן. כדי לוודא שהחיבורים לא מתים לפני השימוש בהם מחדש מומלץ להגדיר את לָקוּחַ עם להשאיר בחיים אסטרטגיה (ראה דוגמה 5.1.).

7. קביעת תצורה של פסק זמן - פסק זמן לשקע באמצעות מנהל החיבורים

פסק הזמן היחיד שניתן להגדיר בזמן הגדרת מנהל החיבור הוא פסק הזמן לשקע:

דוגמא 7.1. הגדרת פסק זמן של שקע ל -5 שניות

מסלול HttpRoute = HttpRoute חדש (HttpHost חדש ("www.baeldung.com", 80)); PoolingHttpClientConnectionManager connManager = PoolingHttpClientConnectionManager חדש (); connManager.setSocketConfig (route.getTargetHost (), SocketConfig.custom (). setSoTimeout (5000) .build ());

לדיון מעמיק יותר על פסק זמן ב- HttpClient - ראה זאת.

8. פינוי חיבור

רגיל פינוי חיבור לזהות חיבורים לא פעילים ופג וסגרו אותם; ישנן שתי אפשרויות לעשות זאת.

  1. מסתמך על HttpClient כדי לבדוק אם החיבור מעופש לפני ביצוע בקשה. זו אפשרות יקרה שלא תמיד אמינה.
  2. צור שרשור צג לסגירת חיבורים סרק ו / או סגורים.

דוגמא 8.1. הגדרת ה- HttpClient כדי לבדוק אם יש חיבורים מעופשים

PoolingHttpClientConnectionManager connManager = PoolingHttpClientConnectionManager חדש (); CloseableHttpClient client = HttpClients.custom (). SetDefaultRequestConfig (RequestConfig.custom (). SetStaleConnectionCheckEnabled (true) .build ()) .setConnectionManager (connManager) .build ();

דוגמא 8.2. באמצעות חוט צג חיבור מעופש

PoolingHttpClientConnectionManager connManager = חדש PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); IdleConnectionMonitorThread staleMonitor = IdleConnectionMonitorThread חדש (connManager); staleMonitor.start (); staleMonitor.join (1000);

ה IdleConnectionMonitorThreadהכיתה רשומה להלן:

מחלקה ציבורית IdleConnectionMonitorThread מאריך אשכול {פרטית סופית HttpClientConnectionManager connMgr; כיבוי בוליאני נדיף פרטי; ציבור IdleConnectionMonitorThread (PoolingHttpClientConnectionManager connectMgr) {super (); this.connMgr = connMgr; } @ עקוף הפעלה בטלנית ציבורית () {נסה {בזמן (! כיבוי) {מסונכרן (זה) {חכה (1000); connMgr.closeExpiredConnections (); connMgr.closeIdleConnections (30, TimeUnit.SECONDS); }}} לתפוס (InterruptedException ex) {shutdown (); }} כיבוי חלל ציבורי () {כיבוי = נכון; מסונכרן (זה) {notifyAll (); }}}

9. סגירת חיבור

ניתן לסגור חיבור בחן (ניסיון לשטוף את מאגר הפלט לפני הסגירה), או בכוח, על ידי קריאה ל לכבות שיטה (מאגר הפלט לא נשטף).

כדי לסגור כראוי קשרים עלינו לעשות את כל הפעולות הבאות:

  • צרכו וסגרו את התגובה (אם ניתן לסגירה)
  • סגור את הלקוח
  • לסגור ולסגור את מנהל החיבורים

דוגמא 8.1. סגירת חיבור ושחרור משאבים

connManager = PoolingHttpClientConnectionManager חדש (); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); HttpGet get = HttpGet new ("// google.com"); CloseableHttpResponse response = client.execute (get); EntityUtils.consume (response.getEntity ()); תגובה.סגור (); client.close (); connManager.close (); 

אם המנהל נסגר מבלי שהחיבורים כבר נסגרו - כל החיבורים ייסגרו וכל המשאבים ישוחררו.

חשוב לזכור כי הדבר לא יזרוק נתונים העשויים להיות מתמשכים עבור החיבורים הקיימים.

10. מסקנה

במאמר זה דנו כיצד להשתמש ב- API לניהול חיבורי HTTP של HttpClient לטיפול את כל תהליך ניהול החיבורים - החל מפתיחתם והקצאתם, דרך ניהול השימוש בהם במקביל על ידי מספר סוכנים, ועד לסגירתם סופית.

ראינו איך BasicHttpClientConnectionManager הוא פתרון פשוט לטיפול בחיבורים בודדים, וכיצד הוא יכול לנהל חיבורים ברמה נמוכה. ראינו גם איך PoolingHttpClientConnectionManager בשילוב עם HttpClient API מספק שימוש יעיל ותואם פרוטוקולים בחיבורי HTTP.


$config[zx-auto] not found$config[zx-overlay] not found