لقد استخدمت مؤخرًا httpclient ونظرت إلى الوثيقة الرسمية: من المتوقع أن تكون تطبيقات httpclient آمنة. يوصى بإعادة استخدام نفس مثيل هذه الفئة لعمليات إعدام الطلبات المتعددة. تعني الترجمة: تنفيذ HTTPCLIENT آمن مؤشر ترابط ويمكنه إعادة استخدام نفس الحالة لتنفيذ طلبات متعددة. عندما نواجه هذا الوصف ، يجب أن نعتقد أن httpclient يحتاج إلى تغليف. نظرًا لأنه يستخدم Boot Spring Boot ، سنجمع بين الحذاء الربيعي لتغليف httpclient.
1. طلب معالج إعادة المحاولة (طلب إعادة إعادة المحاولة)
من أجل جعل آلية الاستثناء المخصصة سارية المفعول ، يجب تنفيذ واجهة httprequestretryhandler ، والرمز كما يلي:
استيراد java.io.ioException ؛ استيراد java.io.InterruptedioException ؛ استيراد java.net.unknownhostexception ؛ استيراد javax.net.ssl.ssslexception ؛ استيراد javax.net.ssl.sslhandshakeException ؛ استيراد org.apache.http.httpencloseRequest ؛ استيراد org.apache.http.httprequest ؛ استيراد org.apache.http.nohttpresponsexception ؛ استيراد org.apache.http.client.httprequestretryhandler ؛ استيراد org.apache.http.client.protocol.httpclientContext ؛ استيراد org.apache.http.conn.connectTimeOutException ؛ استيراد org.apache.http.protocol.httpcontext ؛ استيراد org.springframework.beans.factory.annotation.value ؛ استيراد org.springframework.context.annotation.bean ؛ استيراد org.springframework.context.annotation.configuration ؛ / *** الوصف: آلية معالجة إعادة محاكاة httpclient*/ configuration الفئة العامة myhttprequestretryhandler {value ("$ {httpclient.config.retrytime}") bean public httprequestretryhandler httprequestretryhandler () {// طلب إعادة إعادة المحاكاة int intrime = this.retrytime ؛ إرجاع httprequestretryhandler () {Public Boolean RetryRequest (استثناء IoException ، int executionCount ، httpcontext context) {// لا يتم إعادة إعادة تجهيزه إذا كان عدد المحاكاة الأقصى ، إذا كان عدد إعادة المحاكاة يتجاوز Rبية. }. }. } // مضيف غير معروف إذا (استثناء مثيل unknownHostException) {return false ؛ } // رفض الاتصال إذا (استثناء مثيل ConnectTimeOutException) {return false ؛ } // SSL Trainshake استثناء إذا (استثناء مثيل sslexception) {return false ؛ } httpclientContext clientContext = httpclientContext.adapt (context) ؛ httprequest request = clientContext.getRequest () ؛ if (! (request electionof httpentityencloseRequest)) {return true ؛ } إرجاع خطأ ؛ }} ؛ }} 2. تجميع مدير الاتصال (إدارة تجمعات الاتصال)
يتم استخدام poolinghttpclientConnectionManager لإدارة تجمع اتصال العميل ويمكنه تقديم خدمات لطلبات من سلاسل مؤشرات ترابط متعددة. الرمز كما يلي:
استيراد org.apache.http.config.registry ؛ استيراد org.apache.http.config.registrybuilder ؛ استيراد org.apache.http.conn.socket.connectionsocketFactory ؛ استيراد org.apache.http.conn.socket.layeredConnectionSocketFactory ؛ استيراد org.apache.http.conn.socket.plainconnectionsocketFactory ؛ استيراد org.apache.http.conn.ssl.sslconnectionsocketFactory ؛ استيراد org.apache.http.impl.conn.poolinghttpclientConnectionMonager ؛ استيراد org.springframework.beans.factory.annotation.value ؛ استيراد org.springframework.context.annotation.bean ؛ استيراد org.springframework.context.annotation.configuration ؛ configuration الفئة العامة mypoolinghttpclientConnectionManager { / *** الحد الأقصى لعدد الاتصالات في مجموعة الاتصال* / value ("$ {httpclient.config.connmaxtotal}") private int connmaxtotal = 20 ؛ / ** * */ value ("$ {httpclient.config.maxperroute}") private int maxperRoute = 20 ؛ / ** * وقت بقاء الاتصال ، الوحدة هي s */ value ("$ {httpclient.config.timetolive}") private int timetolive = 60 ؛ bean publichttpclientConnectionManager poolingClientConnectionManager () {poolinghttpclientconnectionmanager poolhttpcconnmanager = new poolinghttpclientConnectionMonager (60 ، timunit.seconds) ؛ // الحد الأقصى لعدد الاتصالات poolhttpcconnmanager.setMaxTotal (this.connmaxtotal) ؛ // Route Radix poolhttpcconnmanager.setDefaultMaxperRoute (this.maxperroute) ؛ إرجاع poolhttpcconnmanager ؛ }} ملاحظة: عندما لم تعد هناك حاجة إلى مثيل HTTPCLIENT ويكون على وشك أن يكون خارج النطاق ، من المهم إغلاق مدير الاتصال الخاص به لضمان أن جميع الاتصالات التي يحتفظ بها المدير نشطة يتم إغلاقها وتحرير موارد النظام التي تخصصها تلك الاتصالات.
إن مُنشئ فئة poolinghttpclientConnectionManager أعلاه هو كما يلي:
pollinghttpclientConnectionManager (النهائي الطويل timetolive ، timeUnit النهائي) {هذا (getDefaultRegistry () ، null ، null ، null ، timetolive ، tunit) ؛ } السجل الثابت الخاص <CannectionSocketFactory> getDefaultRegistry () {return registryBuilder. <ConnectionSocketFactory> create () .register ("http" ، plainconnectionsfactory. } هناك اتصالان أقصىان في تكوين PoolingHttPclientConnectionManager ، والذي يتحكم في إجمالي عدد الاتصال القصوى وأقصى رقم الاتصال لكل مسار على التوالي. إذا لم يكن هناك إعداد صريح ، بشكل افتراضي ، لا يُسمح إلا بما يصل إلى 2 اتصال لكل مسار ، ولا يتجاوز إجمالي عدد الاتصالات 20. هذه القيمة ليست كافية للعديد من التطبيقات ذات التزامن العالي. يجب تعيين القيمة المناسبة وفقًا للوضع الفعلي. تشبه الفكرة حجم تجمع الخيوط. إذا كانت جميع طلبات الاتصال على عنوان URL نفسه ، فيمكن ضبط قيمة MaxPerRoute لتتوافق مع Maxtotal ، بحيث يمكن إعادة استخدام الاتصال بشكل أكثر كفاءة
ملاحظة خاصة: إذا كنت ترغب في إعادة استخدام اتصال ، فيجب عليك جعل موارد النظام التي يشغلها تم إصدارها بشكل صحيح. طريقة الإصدار كما يلي:
إذا كنت تستخدم OutputStream ، فيجب عليك التأكد من كتابة الكيان بأكمله. إذا كانت عبارة عن inputStream ، فيجب أن تتذكر استدعاء inputstream.close () في النهاية. أو استخدم entityUtils.consume (الكيان) أو entityUtils.Consumequietly (الكيان) لجعل الكيان مرهقًا تمامًا (لا يرمي الأخير استثناءات) للقيام بذلك. هناك طريقة tostring في entityUtils ، والتي هي أيضًا مريحة للغاية (سيتم إغلاق Inputstream تلقائيًا في النهاية عند استدعاء هذه الطريقة ، ولكن في عملية الاختبار الفعلية ، لن يتم إصدار الاتصال) ، ولكن لا يمكن استخدامه إلا إذا كان من الممكن تحديد أن الكيان المستلم ليس كبيرًا بشكل خاص. إذا لم يتم استهلاك الكيان بأكمله بالكامل ، فلا يمكن إعادة استخدام الاتصال ، وسرعان ما لن يتم الحصول على مهلة أو كتل الاتصال المتاحة هنا في مجموعة الاتصال (لأن حالة الاتصال ستنقذ دائمًا ، أي أن الحالة المستخدمة). لذلك ، إذا كنت ترغب في إعادة استخدام الاتصال ، فيجب أن تتذكر أن تستهلك الكيان بالكامل. طالما تم اكتشاف EOF من الدفق ، سيتم استدعاء طريقة RELEASECONNECTION لحامل الاتصال تلقائيًا للمعالجة.
3. الاتصال الإستراتيجية على قيد الحياة
لا تحدد مواصفات HTTP المدة التي قد يظل فيها الاتصال المستمر على قيد الحياة. تستخدم بعض خوادم HTTP رؤوس الاحتفاظ غير القياسية للتواصل مع العملاء في الثواني عندما تنوي الحفاظ على الاتصال على جانب الخادم. يمكن httpclient استخدام هذه المعلومات. إذا لم يكن رأس الحفاظ على الحافة موجودًا في الاستجابة ، فإن HTTPClient يفترض أن الاتصال يمكن أن يظل نشطًا إلى أجل غير مسمى. ومع ذلك ، يتم تكوين العديد من خوادم HTTP المستخدمة بشكل عام لحذف الاتصالات المستمرة بعد فترة من الحالة غير النشطة من أجل حفظ موارد النظام دون إخطار العميل. إذا كانت السياسة الافتراضية متفائلة للغاية ، فقد تحتاج إلى توفير سياسة مخصصة للحفاظ على الكود ، فإن الرمز هو كما يلي:
استيراد org.apache.http.headerElement ؛ استيراد org.apache.http.headerElementIrator ؛ استيراد org.apache.http.httpresponse ؛ استيراد org.apache.http.conn.connectekeepalivestrategy ؛ استيراد org.apache.http.message.basicheaderElementatorator ؛ استيراد org.apache.http.protocol.http ؛ استيراد org.apache.http.protocol.httpcontext ؛ استيراد org.springframework.beans.factory.annotation.value ؛ استيراد org.springframework.context.annotation.bean ؛ استيراد org.springframework.context.annotation.configuration ؛ / *** الوصف: سياسة الحفاظ على الاتصال* Author chhliu*/ configuration الفئة العامة myConnecteSkExtErategy {value ("$ {httpclient.config.keepalivetime}") private int keepalivetime = 30 ؛ bean ("connectedAlivestrategy") public connectionkekenkegestrategy connectionkeepalivestrategy () {return new ConnectionSkeverAstrategy () {public long getkeepalivation (httpresponse strained ، httpcontext context) {// honor 'keep alead' headereratorator it = new basiceaderator ( desponse.headeriterator (http.conn_keep_alive)) ؛ بينما (it.hasnext ()) {headerElement He = it.nextElement () ؛ سلسلة param = he.getName () ؛ قيمة السلسلة = he.getValue () ؛ if (value! = null && param.equalsignorecase ("timeout")) {try {return long.parselong (value) * 1000 ؛ } catch (numberFormatexception تجاهل) {}}} إرجاع 30 * 1000 ؛ }} ؛ }} ملاحظة: لا يتم استخدام الاتصالات الطويلة في جميع المواقف ، وخاصة في الوقت الحاضر ، يتم نشر معظم الأنظمة على خوادم متعددة ولديها وظائف موازنة التحميل. إذا حافظنا على اتصال طويل عند الوصول ، بمجرد تعليق هذا الخادم ، فسيؤثر ذلك على العميل. في الوقت نفسه ، لا يمكننا الاستفادة الكاملة من خصائص موازنة التحميل للخادم. بدلاً من ذلك ، تكون الاتصالات القصيرة أكثر فائدة. يجب تحديد هذه وفقًا للاحتياجات المحددة ، بدلاً من تلخيصها في جملة واحدة.
4. تكوين وكيل HTTPClient (تكوين الوكيل)
تستخدم لتكوين الوكيل ، الرمز كما يلي:
استيراد org.apache.http.httphost ؛ استيراد org.apache.http.impl.conn.defaultproxyrouteplanner ؛ استيراد org.springframework.beans.factory.annotation.value ؛ استيراد org.springframework.context.annotation.bean ؛ استيراد org.springframework.context.annotation.configuration ؛ / *** الوصف: httpclient proxy* author chhliu*/ configuration الفئة العامة myDefaultProxyrouteplanner {// عنوان مضيف الوكيل @value ("$ {httpclient.config.proxyhost}") سلسلة خاصة ؛ // رقم منفذ الوكيل value ("$ {httpclient.config.proxyport}") private int proxyport = 8080 ؛ bean public defaultproxyrouteplanner defaultproxyrouteplanner () {httphost proxy = new httphost (this.proxyhost ، this.proxyport) ؛ إرجاع New DefaultProxyRoutePlanner (Proxy) ؛ }} لا يدعم HTTPCLIENT الاتصالات المباشرة البسيطة وسياسات التوجيه المعقدة والوكالة. تعتمد HttProutePlanner على سياق HTTP ، وهي سياسة حوسبة توجيه العميل إلى الخادم. بشكل عام ، إذا لم يكن هناك وكيل ، فلن تحتاج إلى تعيين هذا الشيء. هناك مفهوم حاسم للغاية هنا - الطريق: في httpclient ، يشير المسار إلى سطر من مضيف الجهاز الهدف من البيئة الجارية ، أي إذا كان مضيف عنوان URL المستهدف هو نفسه ، فإن مسارهم هو نفسه.
5. requestConfig
التكوينات المختلفة المستخدمة لتعيين الطلب ، الرمز هو كما يلي:
استيراد org.apache.http.client.config.requestConfig ؛ استيراد org.springframework.beans.factory.annotation.value ؛ استيراد org.springframework.context.annotation.bean ؛ استيراد org.springframework.context.annotation.configuration ؛ Configuration الفئة العامة myRequestConfig {value ("$ {httpclient.config.connecttimeout}") private int connecttimeout = 2000 ؛ value ("$ {httpclient.config.connectrequestTimeOut}") private int connectRequestTimeOut = 2000 ؛ value ("$ {httpclient.config.sockettimeout}") private int sockettimeout = 2000 ؛ bean public requestConfig config () {return requestConfig.custom () .setConnectionRequestTimeout (this.connectrequesttimeout) .setConnectTimeout (this.connecttimeout) .SetSockettimeout (this.sockettimeout) .build () ؛ }} requestConfig هو بعض تكوين الطلب. هناك ثلاث أوقات مهلة أكثر أهمية. افتراضيًا ، تكون جميع أوقات المهلة الثلاثة هي 0 (إذا لم يتم تعيين تكوين الطلب ، فسيتم تعيين المعلمات الافتراضية في getRequestConfig من httpclientparamconfig أثناء التنفيذ). هذا يعني الانتظار اللانهائي ، والذي يمكن أن يتسبب بسهولة في حظر جميع الطلبات والانتظار إلى أجل غير مسمى في هذا المكان. هذه المهلة الثلاث هي:
أ. ConnectionRequestTimeOut - TimeOut للحصول على اتصال من تجمع الاتصال
يحدد هذا الوقت وقت المهلة لاسترداد الاتصال من مجموعة الاتصال التي تديرها ConnectionManager. إذا لم يكن هناك اتصال متاح في مجموعة الاتصال ، فسيتم حظر الطلب ، والحد الأقصى للوقت لانتظار ConnectionRequestTimeout. إذا لم يتم تقديمه ، يتم إلقاء استثناء ConnectionPooltimeOutException ولا يُسمح بأي مزيد من الانتظار.
ب. ConnectTimeOut - وقت المهلة
يحدد هذا الوقت مهلة إنشاء اتصال مع الخادم من خلال الشبكة ، أي وقت انتظار الاتصال للاتصال بعنوان URL المستهدف بعد الحصول على اتصال في تجمع الاتصال. تحدث مهلة ، وسيتم إلقاء استثناء ConnectionTimeOutException.
ج. Sockettimeout - وقت المهلة
يحدد هذا الوقت وقت المهلة لبيانات قراءة المقبس ، أي الوقت الذي يستغرقه الانتظار بعد الاتصال بالخادم للحصول على بيانات استجابة من الخادم ، أو الوقت الذي يستغرقه للحصول على الاستجابة بعد الاتصال بعنوان URL السابق. تحدث مهلة وسيتم طرح استثناء SockettimeOutException.
6
يتم إنشاء مثيل له HTTPClient من خلال تنفيذ Factorybean ، والرمز كما يلي:
استيراد org.apache.http.client.httprequestretryhandler ؛ استيراد org.apache.http.client.config.requestConfig ؛ استيراد org.apache.http.impl.conn.ConnecteeKekekEngalivestrategy ؛ استيراد org.apache.http.impl.client.closablehttpclient ؛ استيراد org.apache.http.impl.client.httpclients ؛ استيراد org.apache.http.impl.conn.defaultproxyrouteplanner ؛ استيراد org.apache.http.impl.conn.poolinghttpclientConnectionMonager ؛ استيراد org.springframework.beans.factory.disposableBean ؛ استيراد org.springframework.beans.factory.factorybean ؛ استيراد org.springframework.beans.factory.initializingbean ؛ استيراد org.springframework.beans.factory.annotation.autowired ؛ استيراد org.springframework.stereotype.service ؛ / *** الوصف: تغليف عميل httpclient*/ service ("httpclientmanagerfactoryben") الطبقة العامة httpclientmanagerfactoryben تنفذ FactoryBean <ClosibleHtpClient> ، التهيئة ، disposablebean {/ *** compliced completed by factorient*/ private close. AUTOWIRED PRIVATE CONNECTIONKESTRATIONGESTRATEGYSY CONNECTIONKEKENTYSTY ؛ @autowired httprequestretryhandler httprequestretryhandler ؛ @autowired private defaultproxyrouteplanner proxyrouteplanner ؛ autowired poolinghttpclientConnectionManager poolhttpcconnmanager ؛ Autowired requestConfig config ؛ // عند تدمير السياق ، فإن تدمير مثيل httpclient override public void Dorting () يلقي الاستثناء { / * * calling httpclient.close () سيغلق أولاً مدير الاتصال ، ثم تحرير جميع الموارد التي تشغلها httpclient ، * تغلق جميع الاتصالات المستخدمة أو idle ، بما في ذلك الصوف الأساسي. نظرًا لأن مدير الاتصال الذي يستخدمه يتم إغلاقه هنا ، * لذلك عندما يتعين عليك تقديم طلب HTTP في المرة القادمة ، يجب عليك تجديد مدير اتصال لإنشاء httpclient ، * أي عندما تحتاج إلى الإغلاق وإنشاء عميل جديد ، لا يمكن أن يكون مدير الاتصال مفردة. */ if (null! = this.client) {this.client.close () ؛ }} @override // تهيئة مثيل الفراغ العام بعد propertiesset () يلقي استثناء {/ * * يوصى باستخدام httpclients.custom لإنشاء httpclientbuilder هنا ، بدلاً من استخدام الوثائق الرسمية ، httpcreate () غير آمن ، ولكن httpclients هو في الواقع قابلة للتهاب. لا يمكن للكائن غير القابل للتغيير التأكد فقط من عدم تغيير حالة الكائن ، ولكن يمكن أيضًا مشاركتها بواسطة مؤشرات ترابط أخرى دون استخدام آلية القفل */ this.client = httpclients.custom (). .setRoutePlanner (proxyrouteplanner) .SetDefaultRequestConfig (config) .build () ؛ }. } Override public class <؟> getObjectType () {return (this.client == null؟ closablehttpclient.class: this.client.getClass ()) ؛ }. }}7. إضافة ملفات التكوين
# Proxy Host HttpClient.config.proxyhost = xxx.xx.xx.xx # proxy port httpclient الاتصالات في تجمع الاتصال httpclient.config.connmaxtotal = 20 httpclient.config.maxperroute = 20 # مهلة الاتصال ، وحدة ms httpclient.config.connecttimeout = 20 # timeout ، الوحدة هي ms httpclient.config.connecttime httpclient.config.connectrequesttimeout = 2000 # sock timeout httpclient.config.sockettimeout = 2000 # connection time ، unit s httpclient.config.timetolive = 60
8. اختبار
رمز الاختبار كما يلي:
استيراد java.io.ioException ؛ استيراد java.util.concurrent.executorservice ؛ استيراد java.util.concurrent.executors ؛ استيراد javax.annotation.Resource ؛ استيراد org.apache.http.consts ؛ استيراد org.apache.http.parseException ؛ استيراد org.apache.http.client.clientprotocolexception ؛ استيراد org.apache.http.client.methods.closablehttpresponse ؛ استيراد org.apache.http.client.methods.httpget ؛ استيراد org.apache.http.impl.client.closablehttpclient ؛ استيراد org.apache.http.util.entityUtils ؛ استيراد org.junit.test ؛ استيراد org.junit.runner.runwith ؛ استيراد org.springframework.boot.test.context.springBoottest ؛ استيراد org.springframework.test.context.junit4.springRunner ؛ Runwith (SpringRunner.Class) springboottest فئة عامة httpclientManageRfactoryBentest {// enjuct httpclient extom @reresource (name = "httpclientManagerFactoryben") private closablehtpclient ؛ Test public void test () يلقي ClientProtocolexception ، ioException ، interruptedException {ExecutorService Service = Executors.NewFixedThreadPool (2) ؛ لـ (int i = 0 ؛ i <10 ؛ i ++) {service.submit (new runNable () {Override public void run () {system.out.println ("الخيط الحالي هو: httpget ("https: // localhost: 8080/testjson") ؛ System.out.println("======================================================================================; EntityUtils.consumeQuietly(entity);// Release the Connects (clientProtocception e) {E.PrintStackTrace () ؛ }) ؛ من خلال الخطوات المذكورة أعلاه ، يتم إكمال تغليف HTTPClient بشكل أساسي. إذا كنت بحاجة إلى أن تكون أكثر تفصيلًا ، فيمكنك تحسين HTTPClient تدريجياً مثل HTTPClientTemplate وفقًا للأفكار المذكورة أعلاه ، لأنه يستخدم ClosableHTTPclient آلية رد اتصال داخليًا ، والتي تشبه JDBCTEMPLATE أو REDISTEMPHITION حتى يمكن توفير الخدمة في شكل بدء تشغيل الربيع.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.