J'ai récemment utilisé HTTPClient et j'ai examiné le document officiel: les implémentations HTTPClient devraient être sûres. Il est recommandé que la même instance de cette classe soit réutilisée pour plusieurs exécutions de demande. La traduction signifie: l'implémentation de HttpClient est thread-safe et peut réutiliser la même instance pour exécuter plusieurs demandes. Lorsque nous rencontrons cette description, nous devons penser que HttpClient doit être encapsulé. Puisqu'il est utilisé Spring Boot, nous combinerons le démarrage de ressort pour encapsuler httpclient.
1. Demandez le gestionnaire de réessayer (demande de réessayer)
Afin de faire en sorte que le mécanisme d'exception personnalisé prenne effet, l'interface httprequestretryHandler doit être implémentée, le code est le suivant:
Importer java.io.ioException; Importer java.io.InterrupteDioException; import java.net.unknownhostException; Importer javax.net.ssl.sslexception; Importer javax.net.ssl.sslhandshakeException; import org.apache.http.httpenclosingRequest; import org.apache.http.httprequest; import org.apache.http.NoHttpResponseException; import org.apache.http.client.httprequestretryHandler; import org.apache.http.client.protocol.httpclientContext; import org.apache.http.conn.connectTimeoutException; import org.apache.http.protocol.httpContext; import org.springframework.beans.factory.annotation.value; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; / ** * Description: Mécanisme de traitement de réchauffement de HttpClient * / @configuration classe publique MyHttpRequestRetryHandler {@Value ("$ {httpclient.config.retrytime}") // Ici, il est recommandé d'utiliser @configuration ") Méthode de reverse; @Bean public httprequestretryHandler httpRequestRetryHandler () {// request retry final int rerytime = this.retrytime; return new httprequestretryHandler () {public boolean reryRequest (ioException exception, int ExecutionCount, httpContext context) {// ne pas reessée si le nombre de réchauffement maximum, si le nombre de retrys dépasse le temps de redressement, la demande ne sera plus rétractée si (exécutionCount> = rerytimetime) {retourne false; } // Le serveur rompt l'exception de connexion du client if (exception instanceof nohttpResponseException) {return true; } // Time Out Timeout Retry if (exception instanceof interrupteIoException) {return true; } // hôte inconnu if (instance exceptionof inconnuehostException) {return false; } // Connexion refusée if (exception instanceof connectTimeOutException) {return false; } // exception de poignée de main ssl if (exception instanceof sslexception) {return false; } HttpClientContext clientContext = httpclientContext.adapt (context); HttpRequest request = clientContext.getRequest (); if (! (Demande d'instance de httpentityenclosingrequest)) {return true; } return false; }}; }} 2. Gestionnaire de connexion de la mise en commun (gestion de pool de connexions)
PooringHttpClientConnectionManager est utilisé pour gérer le pool de connexion du client et peut fournir des services pour les demandes de plusieurs threads. Le code est le suivant:
import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.connectionsocketfactory; import org.apache.http.conn.socket.layeredConnectionSocketFactory; import org.apache.http.conn.socket.plainConnectionSocketFactory; import org.apache.http.conn.ssl.sslconnectionsocketfactory; import org.apache.http.impl.conn.poolinghttpclientConnectionManager; import org.springframework.beans.factory.annotation.value; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; @Configuration publique classe mypoolingHttpClientConnectionManager {/ ** * Nombre maximum de connexions dans le pool de connexions * / @value ("$ {httpclient.config.connmaxtotal}") private int crormmextotal = 20; / ** * * / @value ("$ {httpclient.config.maxperRoute}") private int maxperRoute = 20; / ** * Temps de survie de la connexion, l'unité est s * / @value ("$ {httpclient.config.timétolive}") private int titrolive = 60; @Bean Public PooringHttpClientConnectionManager PooringClientConnectionManager () {PooringHttpClientConnectionManager PoolHttpcConnManager = new PooringHttpClientConnectionManager (60, timeunit.seconds); // Nombre maximum de connexions PoolHttpcConnManager.setMextotal (this.ConnMextotal); // Route Radix PoolHttpcConnManager.SetDefaultMaxPerRoute (this.maxperRoute); Retour PoolHttpcConnManager; }} Remarque: Lorsque l'instance HTTPClient n'est plus nécessaire et est sur le point d'être hors de portée, il est important de fermer son gestionnaire de connexions pour s'assurer que toutes les connexions que le gestionnaire maintiennent actives sont fermées et libèrent les ressources système allouées par ces connexions.
Le constructeur de la classe de mise en commun de la mise en poolhttpclientConnectionManager est la suivante:
Public PooringHttpClientConnectionManager (final long timetolive, final timeunit tunit) {this (getDefaulTregistry (), null, null, null, timetolive, tunit); } Registre statique privé <ConnectionsOcketFactory> GetDefaulTregistry () {return RegistryBuilder. <ConnectionsOcketFactory> Create () .Register ("http", PlainConnectionSocketFactory.getsocketFactory.getSocket .Register ("https", SslConnectionSetfactory.GetSocketFactory ()). } Il y a deux connexions maximales dans la configuration de la configuration de la mise en commun, qui contrôle respectivement le numéro de connexion maximum total et le nombre maximal de connexion par route. S'il n'y a pas de paramètre explicite, par défaut, seulement jusqu'à 2 connexions par itinéraire sont autorisées, et le nombre total de connexions ne dépasse pas 20. Cette valeur n'est pas suffisante pour de nombreuses applications avec une concurrence élevée. La valeur appropriée doit être fixée en fonction de la situation réelle. L'idée est similaire à la taille du pool de threads. Si toutes les demandes de connexion sont à la même URL, la valeur de MaxperRoute peut être définie pour être cohérente avec MAXTOTAL, afin que la connexion puisse être réutilisée plus efficacement
Remarque spéciale: si vous souhaitez réutiliser une connexion, vous devez faire correctement les ressources système qu'il occupe correctement. La méthode de libération est la suivante:
Si vous utilisez OutputStream, vous devez vous assurer que l'entité entière est écrite. S'il s'agit d'un InputStream, vous devez vous rappeler d'appeler InputStream.Close () à la fin. Ou utiliser entityutils.consume (entité) ou entityutils.consumequiely (entité) pour rendre l'entité complètement épuisée (ce dernier ne lance pas des exceptions) pour ce faire. Il existe une méthode de toString dans les entityutiles, ce qui est également très pratique (le SputStream sera automatiquement fermé à la fin lors de l'appel de cette méthode, mais dans le processus de test réel, la connexion ne sera pas libérée), mais elle ne peut être utilisée que s'il peut être déterminé que l'entité reçue n'est pas particulièrement importante. Si l'entité entière n'est pas entièrement consommée, la connexion ne peut pas être réutilisée et bientôt le délai d'expiration ou les blocs disponibles ici ne sera pas obtenu dans le pool de connexion (car l'état de la connexion sera toujours épargné, c'est-à-dire que l'État est utilisé). Par conséquent, si vous souhaitez réutiliser la connexion, vous devez vous rappeler de consommer pleinement l'entité. Tant que l'EOF du flux est détecté, la méthode de connexion de relance du titulaire de connexion sera automatiquement appelée pour le traitement.
3. Connexion Keep Alive Strategy
La spécification HTTP ne spécifie pas la durée de la durée d'une connexion persistante et doit rester en vie. Certains serveurs HTTP utilisent des en-têtes de main-d'œuvre non standard pour communiquer aux clients la période en quelques secondes lorsqu'ils ont l'intention de garder la connexion côté serveur. HttpClient peut utiliser ces informations. Si l'en-tête de conservation n'existe pas dans la réponse, HttpClient suppose que la connexion peut rester active indéfiniment. Cependant, de nombreux serveurs HTTP généralement utilisés sont configurés pour supprimer des connexions persistantes après une période de statut inactif afin d'économiser des ressources système sans notifier le client. Si la politique par défaut est trop optimiste, vous devrez peut-être fournir une politique de conservation personnalisée, le code est le suivant:
import org.apache.http.headErement; import org.apache.http.Http.Http.EleMementierator; import org.apache.http.httpResponse; import org.apache.http.conn.ConnectionkeepaliVestrategy; import org.apache.http.message.basicheDereEmementierator; import org.apache.http.protocol.http; import org.apache.http.protocol.httpContext; import org.springframework.beans.factory.annotation.value; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; / ** * Description: Politique de maintien de la connexion * @author chhliu * / @configuration classe publique MyConnectionkeectionEnkeVivStrategy {@value ("$ {httpclient.config.keepalivetime}") private intHeepaliveTime = 30; @Bean ("ConnectionkeepaliVestragy") Connexion de connexion publique Connexion ConnectionEnekeViVeStrategy () {return new ConnectionKeepaliVestragy () {public long getKekeveLiveduration (HttpResponse Response, httpContex Response.HedeRiterator (http.conn_keep_alive)); while (it.hasnext ()) {HeaderElement he = it.nextElement (); String param = he.getName (); String Value = He.getValue (); if (value! = null && param.equalsignorecase ("timeout")) {try {return long.parselong (value) * 1000; } catch (NumberFormatexception ignore) {}}} return 30 * 1000; }}; }} Remarque: les connexions longues ne sont pas utilisées dans toutes les situations, en particulier de nos jours, la plupart des systèmes sont déployés sur plusieurs serveurs et ont des fonctions d'équilibrage de charge. Si nous conservons une longue connexion lors de l'accès, une fois ce serveur en suspension, cela affectera le client. Dans le même temps, nous ne pouvons pas utiliser pleinement les caractéristiques d'équilibrage de la charge du serveur. Au lieu de cela, les connexions courtes sont plus bénéfiques. Ceux-ci doivent être déterminés en fonction des besoins spécifiques, plutôt que de les résumer en une phrase.
4. Configuration proxy httpclient (configuration proxy)
Utilisé pour configurer le proxy, le code est le suivant:
import org.apache.http.httphost; import org.apache.http.impl.conn.defaultProxyRoutePlanner; import org.springframework.beans.factory.annotation.value; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; / ** * Description: HttpClient Proxy * @Author Chhliu * / @configuration public class MyDefaultProxyRoutePlanner {// L'adresse hôte du proxy @value ("$ {httpclient.config.proxyhost}") String privé proxyhost; // le numéro de port du proxy @value ("$ {httpclient.config.proxyport}") private int proxyport = 8080; @Bean public DefaultProxyRoutePlanner DefaultProxyRoutePlanner () {httphost proxy = new httphost (this.proxyhost, this.proxyport); return newproxyroutleplanner (proxy); }} HTTPClient prend non seulement des connexions directes simples, des politiques de routage complexes et de la proxyme. HttpRouttePlanner est basé sur le contexte HTTP, la stratégie informatique de routage client-serveur. Généralement, s'il n'y a pas de proxy, vous n'avez pas besoin de définir cette chose. Il y a un concept très critique ici - Route: Dans HttpClient, un itinéraire fait référence à une ligne de la machine à environnement en cours d'exécution, hôte de la machine cible, c'est-à-dire si l'hôte de l'URL cible est le même, alors leur itinéraire est le même.
5. demandesconfig
Les différentes configurations utilisées pour définir la demande, le code est le suivant:
import org.apache.http.client.config.requestConfig; import org.springframework.beans.factory.annotation.value; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; @Configuration public class myRequestConfig {@Value ("$ {httpclient.config.connectTimeout}") private int connectTimeout = 2000; @Value ("$ {httpclient.config.connectElestTimeout}") private int ConnectElestTimeout = 2000; @Value ("$ {httpclient.config.sockettimeout}") private int sockettimeout = 2000; @Bean public requestConfig config () {return requestConfig.custom () .setConnectionRequestTimeout (this.connectElestTimeout) .setConnectTimeout (this.connectTimeout) .setsocketTimeout (this.sockettimeout) .build (); }} RequestConfig est une configuration de la demande. Il y a trois temps de délai qui sont plus importants. Par défaut, les trois temps de temps mort sont 0 (si la configuration de la demande n'est pas définie, les paramètres par défaut seront définis dans GetRequestConfig de HttpClientParamConfig pendant l'exécution). Cela signifie une attente infinie, ce qui peut facilement faire bloquer et attendre toutes les demandes indéfiniment à cet endroit. Ces trois délais d'attente sont:
un. ConnectionRequestTimeout - Timeout pour obtenir la connexion à partir du pool de connexion
Cette fois définit l'heure du délai d'expiration pour récupérer la connexion à partir du pool de connexion géré par ConnectionManager. S'il n'y a pas de connexion disponible dans le pool de connexion, la demande sera bloquée et le temps maximum pour attendre la connexion de connexion. S'il n'a pas été servi, une exception ConnectionPoolTimeOnexception est lancée et aucune autre attente n'est autorisée.
né ConnectTimeout - temps de délai de connexion
Cette fois définit le délai d'expiration pour établir une connexion avec le serveur via le réseau, c'est-à-dire le temps d'attente de connexion pour se connecter à l'URL cible après avoir obtenu une connexion dans le pool de connexion. Un délai d'expiration se produit, une exception ConnectionTimeOutException sera lancée.
c. sockettimeout - temps de délai d'expiration
Cette fois définit l'heure du délai d'expiration pour les données de lecture de socket, c'est-à-dire le temps nécessaire pour attendre après la connexion au serveur pour obtenir des données de réponse du serveur, ou le temps nécessaire pour obtenir la réponse après la connexion à l'URL précédente. Un délai d'expiration se produit et une exception SockettimeoutException sera lancée.
6. Instancier httpclient
Le HTTPClient est instancié en implémentant FactoryBean, le code est le suivant:
import org.apache.http.client.httprequestretryHandler; import org.apache.http.client.config.requestConfig; import org.apache.http.impl.conn.Connection GardenVivesty; import org.apache.http.impl.client.closeablehttpclient; import org.apache.http.impl.client.httpclients; import org.apache.http.impl.conn.defaultProxyRoutePlanner; import org.apache.http.impl.conn.poolinghttpclientConnectionManager; import org.springframework.beans.factory.disposableBean; import org.springframework.beans.factory.factoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.sterreotype.service; / ** * Description: HttpClient Client Encapsulation * / @Service ("HttpClientManagerFactoryBen") Classe publique HttpClientManagerFactoryBen implémente FactoryBean <CloseableHttpClient>, InitializingBean, DisposableBean {/ ** objet cible généré par FactoryBean * / Private SproseableHTTTPCLIEM @Autowired Private ConnectionkeeAndivestgy ConnectionEnkeSaliveStrategy; @Autowired Private HttpRequestretryHandler httpRequestretryHandler; @Autowired Private DefaultProxyRoutePlanner ProxyRovePlanner; @Autowired Private PooringHttpClientConnectionManager PoolHttpcConnManager; @Autowired private requestConfig config; // Lors de la destruction du contexte, la destruction de l'instance httpclient @Override public void destrust () lève l'exception {/ * * appeler httpclient.close () fermera d'abord le gestionnaire de connexions, puis libérer toutes les ressources occupées par le httpclient, * fermera toutes les connexions qui sont utilisées ou inactifs, y compris la prise sous-jacente. Étant donné que le gestionnaire de connexion qu'il utilise est fermé ici, * donc lorsque vous devez faire une demande HTTP la prochaine fois, vous devez renouveler un gestionnaire de connexion pour créer un HTTPClient, * c'est-à-dire lorsque vous devez fermer et créer un nouveau client, le gestionnaire de connexion ne peut pas être un singleton. * / if (null! = this.client) {this.client.close (); }} @ Override // Initialise l'instance publique void AfterProperTeset () lève une exception {/ * * Il est recommandé d'utiliser ici httpclientbuilder.create () pour créer HttplientBuilder * Non-thread-sécurisé, mais HttpClients est en effet iMUTABLE. L'objet immuable peut non seulement s'assurer que l'état de l'objet n'est pas modifié *, mais peut également être partagé par d'autres threads sans utiliser le mécanisme de verrouillage * / this.client = httpclients.custom (). SetConnectionManager (PoolHttpcConConCakeAr) .SetTretryhandler (HttprequestreryHandle .SetRoutePlanner (proxyroutleplanner) .setDefaulTReQuestConfig (config) .build (); } // Renvoie le type de l'instance @Override public closeablehttpclient getObject () lève une exception {return this.client; } @Override public class <?> GetObjectType () {return (this.client == null? Closablehttpclient.class: this.client.getClass ()); } // L'instance construite est un singleton @ override public boolean issingleton () {return true; }}7. Ajouter des fichiers de configuration
# Host de Proxy httpclient.config.proxyhost = xxx.xx.xx.xx # Port proxy httpclient.config.proxyport = 8080 # Timeout de connexion ou exception Retry Times HttpClient.config.retryTime = 3 # Connexion Long, Unit est s httpcl Connexions dans le pool de connexions httpclient.config.connmaxtotal = 20 httpclient.config.maxperRoute = 20 # délai de connexion, unité MS httpclient.config.connectTimeout = 20 # Connection Connection Pool Request Timeout httpclient.connet httpclient.config.connectElestTimeout = 2000 # SOCK Timeout httpclient.config.sockettimeout = 2000 # Temps de survie de la connexion, unité s httpclient.config.timetolive = 60
8. Test
Le code de test est le suivant:
Importer java.io.ioException; Importer java.util.concurrent.executorService; Importer java.util.concurrent.executors; import javax.annotation.resource; import org.apache.http.consts; import org.apache.http.parseException; import org.apache.http.client.clientprotocolexception; import org.apache.http.client.methods.closeablehttpResponse; import org.apache.http.client.methods.httpget; import org.apache.http.impl.client.closeablehttpclient; import org.apache.http.util.entityutils; import org.junit.test; import org.junit.runner.runwith; import org.springframework.boot.test.context.springboottest; import org.springframework.test.context.junit4.springrunner; @Runwith (springrunner.class) @springboottest public class httpclientManagerFactoryBentest {// inject httpclient instance @resource (name = "httpclientManagerFactoryBen") Private CloseableHttpClient client; @Test public void test () lève ClientProtoCoLexception, ioException, InterruptedException {ExecutorService Service = exécutor.NewFixEdThreadPool (2); pour (int i = 0; i <10; i ++) {Service.Submit (new Runnable () {@Override public void run () {System.out.println ("Le thread actuel est:" + thread.currentThread (). Httpget ("https: // localhost: 8080 / testjson"); System.out.println("======================================================================================; EntityUtils.consumeQuietly(entity);// Release the Connexion} Catch (ClientProtoColException E) {E.PrintStackTrace ();} Catch (PareException E) {E.PrintStackTrace (); });} Thread.Sleep (60000); Grâce aux étapes ci-dessus, l'encapsulation de HttpClient est essentiellement terminée. Si vous devez être plus détaillé, vous pouvez progressivement améliorer le HTTPClient en tant que HttpClientTemplate en fonction des idées ci-dessus, car CloserableHttpClient utilise un mécanisme de rappel en interne, ce qui est similaire à JDBCTemplate ou Redetemplate jusqu'à ce que le service puisse être fourni sous la forme de starter de démarrage de printemps.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.