Recientemente usé httpclient y analicé el documento oficial: se espera que las implementaciones de httpclient sean seguras. Se recomienda que se reutilice la misma instancia de esta clase para las ejecuciones de múltiples solicitudes. La traducción significa: la implementación de HTTPClient es segura de hilo y puede reutilizar la misma instancia para ejecutar múltiples solicitudes. Cuando encontramos esta descripción, debemos pensar que HttpClient necesita ser encapsulado. Dado que se usa Boot Spring, combinaremos el arranque de primavera para encapsular httpclient.
1. Solicitar el controlador de reintento (solicitar el procesamiento de reintentos)
Para hacer que el mecanismo de excepción personalizado entre en vigencia, la interfaz HttpRequestryryHandler debe implementarse, el código es el siguiente:
import java.io.ioException; import java.io.interruptedioException; import java.net.unknownHosTexception; import javax.net.ssl.sslexception; import javax.net.ssl.sslhandshakeException; importar org.apache.http.httpenclosingRequest; importar org.apache.http.httprequest; importar org.apache.http.nohttpResponseException; importar org.apache.http.client.httprequestretryhandler; importar org.apache.http.client.protocol.httpclientContext; importar org.apache.http.conn.connecttimeOutException; importar org.apache.http.protocol.httpcontext; importar org.springframework.beans.factory.annotation.value; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; / *** Descripción: Mecanismo de procesamiento de reintento de HttpClient*/ @Configuration clase pública myHttprequestretryHandler {@Value ("$ {httpclient.config.rcrytime}") // aquí se recomienda usar @configurationPerties (prefix = "httpclient") método de retryatate de la intención de retryatate de la intención de retryatate de la intención de retryatate; @Bean public httprequestretryHandler httprequestretryHandler () {// solicitar retry final int retryTtime = this.retrytime; return new httpRequestretryHandler () {public boolean retryRequest (excepción ioException, int ExecutionCount, httpContext context) {// no volver a roya si es un recuento de retry max, si el número de RETYN excede retryTime, la solicitud no se volverá a justificar si (ejecutarCount> = retryTime) {return false; } // El servidor rompe la excepción de conexión del cliente if (excepción instancea de nohttpresponseexception) {return true; } // tiempo de espera de tiempo de espera if (excepcion instancia de interruptedioException) {return true; } // host desconocido if (excepcion instancia de desconocidoHosTexception) {return false; } // conexión rechazada if (excepcion instancia de conectionTimeOutException) {return false; } // excepción de ssl handshake if (excepción instanceo de sslexception) {return false; } HttpClientContext ClientContext = httpClientContext.Adapt (context); Httprequest request = clientContext.getRequest (); if (! (solicite instancia de httpentityEncLoseRequest)) {return true; } return false; }}; }} 2. Administrador de conexión de agrupación (Gestión del grupo de conexión)
PoolinghttpClientConnectionManager se utiliza para administrar el grupo de conexión del cliente y puede proporcionar servicios para solicitudes de múltiples hilos. El código es el siguiente:
importar org.apache.http.config.registry; importar org.apache.http.config.registryBuilder; importar org.apache.http.conn.socket.connectionsockFactory; importar org.apache.http.conn.socket.layeredConnectionSockyFactory; importar org.apache.http.conn.socket.PlainConnectionSockyFactory; importar org.apache.http.conn.ssl.sslconnectionsockyFactory; importar org.apache.http.impl.conn.poolinghttpClientConnectionManager; importar org.springframework.beans.factory.annotation.value; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; @Configuration Clase pública mypoolinghttpClientConnectionManager { / *** Número máximo de conexiones en el grupo de conexiones* / @Value ("$ {httpclient.config.connmaxtotal}") private int connmoxtotal = 20; / ** * */ @Value ("$ {httpclient.config.maxperroute}") private int maxperroute = 20; / ** * Tiempo de supervivencia de conexión, la unidad es s */ @Value ("$ {httpclient.config.timetolive}") private int timetolive = 60; @Bean Public PoolinghttpClientConnectionManager PoolingClientConnectionManager () {PoolingHttpClientConnectionManager PoolHttpcConnManager = new PoolingHttpClientConnectionManager (60, TimeUnit.SeConds); // Número máximo de conexiones Poolhttpcconnmanager.setMaxTotal (this.connmAxtotal); // ruta Radix Poolhttpcconnmanager.setDefaultMaxperRoute (this.maxperroute); return Poolhttpcconnmanager; }} Nota: Cuando la instancia httpclient ya no es necesaria y está a punto de estar fuera de alcance, es importante cerrar su administrador de conexión para garantizar que todas las conexiones que el administrador mantenga activo esté cerrada y liberarse de los recursos del sistema asignados por esas conexiones.
El constructor de la clase de PoolinghttpClientConnectionManager de arriba es el siguiente:
Public PoolinghttpClientConnectionManager (Timetolive largo final, Tunit Final TimeUnit) {this (getDefaulTregistry (), nulo, nulo, nulo, timetolive, tunit); } Registro estático privado <nectificationSockFactory> getDefaulTregistry () {return RegistryBuilder. <ConnectionSockEcfactory> create () .Register ("http", plainconnectionsocketfactory.getSocketory ()) .register ("https", sslconnectsockyFactory.getSocketFactory () .Build (); } Hay dos conexiones máximas en la configuración de PoolinghttpClientConnectionManager, que controlan el número de conexión máximo total y el número de conexión máximo por ruta respectivamente. Si no hay una configuración explícita, por defecto, solo se permiten hasta 2 conexiones por ruta, y el número total de conexiones no excede los 20. Este valor no es suficiente para muchas aplicaciones con alta concurrencia. El valor apropiado debe establecerse de acuerdo con la situación real. La idea es similar al tamaño del grupo de hilos. Si todas las solicitudes de conexión están a la misma URL, el valor de MaxperRoute puede establecerse para ser consistente con Maxtotal, de modo que la conexión se pueda reutilizar de manera más eficiente
Nota especial: si desea reutilizar una conexión, debe hacer los recursos del sistema que ocupa correctamente liberados. El método de lanzamiento es el siguiente:
Si está utilizando OutputStream, debe asegurarse de que toda la entidad esté escrita. Si es un InputStream, debe recordar llamar a InputStream.close () al final. O utilice entityUtil.sume (entidad) o entityutilil.consumequiety (entidad) para hacer que la entidad sea completamente agotada (esta última no arroja excepciones) para hacer esto. Existe un método de tostración en EntityUtils, que también es muy conveniente (el InputStream se cerrará automáticamente al final al llamar a este método, pero en el proceso de prueba real, la conexión no se liberará), pero solo se puede usar si se puede determinar que la entidad recibida no es particularmente grande. Si toda la entidad no se consume por completo, la conexión no se puede reutilizar, y pronto el tiempo de espera o bloques de conexión disponible aquí no se obtendrá en el grupo de conexión (porque el estado de la conexión siempre se salvará, es decir, el estado que se usa). Por lo tanto, si desea reutilizar la conexión, debe recordar consumir la entidad completamente. Mientras se detecte el EOF de la transmisión, el método ReleaseConnection del titular de la conexión se solicitará automáticamente para el procesamiento.
3. Conexión Mantenga la estrategia Alive
La especificación HTTP no especifica cuánto tiempo puede y debe permanecer viva una conexión persistente. Algunos servidores HTTP usan encabezados no estándar para comunicar a los clientes el período en segundos cuando tienen la intención de mantener la conexión en el lado del servidor. HttpClient puede usar esta información. Si el encabezado Keep-Alive no existe en la respuesta, HttpClient supone que la conexión puede permanecer activa indefinidamente. Sin embargo, muchos servidores HTTP generalmente utilizados están configurados para eliminar conexiones persistentes después de un período de estado inactivo para ahorrar recursos del sistema sin notificar al cliente. Si la política predeterminada es demasiado optimista, es posible que deba proporcionar una política personalizada Keep-Alive, el código es el siguiente:
importar org.apache.http.headerelement; importar org.apache.http.headerElementIterator; importar org.apache.http.httpresponse; importar org.apache.http.conn.connectionkeepalivestrategy; importar org.apache.http.message.basicheadereLementIterator; importar org.apache.http.protocol.http; importar org.apache.http.protocol.httpcontext; importar org.springframework.beans.factory.annotation.value; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; / *** Descripción: Política de mantenimiento de la conexión* @author chhliu*/ @configuration clase pública myconnectionkeepalivestrategy {@Value ("$ {httpclient.config.keepaliveTime}") private int keepAliveTime = 30; @Bean("connectionKeepAliveStrategy") public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // Honor 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( respuesta.headeriterator (http.conn_keep_alive)); while (it.hasnext ()) {HeaderElement He = it.nextelement (); Cadena param = he.getName (); Valor de cadena = he.getValue (); if (value! = null && param.equalSignorecase ("timeOut")) {try {return Long.parselong (valor) * 1000; } catch (numberFormateException ignore) {}}} return 30 * 1000; }}; }} Nota: Las conexiones largas no se usan en todas las situaciones, especialmente hoy en día, la mayoría de los sistemas se implementan en múltiples servidores y tienen funciones de equilibrio de carga. Si mantenemos una conexión larga al acceder, una vez que se suspenda ese servidor, afectará al cliente. Al mismo tiempo, no podemos utilizar completamente las características de equilibrio de carga del servidor. En cambio, las conexiones cortas son más beneficiosas. Estos deben determinarse de acuerdo con necesidades específicas, en lugar de resumirlas en una oración.
4. Configuración proxy de httpclient (configuración proxy)
Utilizado para configurar el proxy, el código es el siguiente:
importar org.apache.http.httphost; importar org.apache.http.impl.conn.defaultProxyRoutePlanner; importar org.springframework.beans.factory.annotation.value; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; / *** Descripción: httpclient proxy* @author chhliu*/ @configuration public class myDefaultProxyRoutePlanner {// La dirección de host del proxy @Value ("$ {httpclient.config.proxyhost}") Private String pro Proxyhost; // El número de puerto del proxy @Value ("$ {httpclient.config.proxyport}") private int proxyport = 8080; @Bean public defaultProxyRoutePlanner DefaultProxyRoutePlanner () {httphost proxy = new httphost (this.proxyhost, this.proxyport); devolver nuevo defaultProxyRoutePlanner (proxy); }} HttpClient no solo admite conexiones directas simples, políticas de enrutamiento complejas y proxy. HTTPROUTEPLANNER se basa en el contexto HTTP, la política informática de enrutamiento de cliente a servir. En general, si no hay proxy, no necesita establecer esto. Aquí hay un concepto muy crítico: ruta: en httpclient, una ruta se refiere a una línea de la máquina de entorno en ejecución,> host de máquina de destino, es decir, si el host de la URL de destino es la misma, entonces su ruta es la misma.
5. RequestConfig
Las diversas configuraciones utilizadas para establecer la solicitud, el código es el siguiente:
importar org.apache.http.client.config.requestconfig; importar org.springframework.beans.factory.annotation.value; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; @Configuration Class public MyRequestConfig {@Value ("$ {httpclient.config.connecttimeout}") private int conectenttimeout = 2000; @Value ("$ {httpclient.config.connectricesttimeout}") private int connectriceCtimtimeOut = 2000; @Value ("$ {httpclient.config.sockettimeout}") private int socketTimeout = 2000; @Bean Public RequestConfig config () {return requestConfig.custom () .SetConnectionRequestTimeOut (this.ConnectruCeestTimeOut) .SetConnectTimeOut (this.ConnectTimeOut) .SetSocketTimeout (this.socketTimeout) .Build (); }} RequestConfig es una configuración de solicitud. Hay tres tiempos de tiempo de espera que son más importantes. De forma predeterminada, los tres tiempos de tiempo de espera son 0 (si no se establece la configuración de la solicitud, los parámetros predeterminados se establecerán en HttpClientParamConfig GetRequestConfig durante la ejecución). Esto significa una espera infinita, lo que puede hacer que todas las solicitudes se bloqueen y esperen indefinidamente en este lugar. Estos tres tiempos de espera son:
a. ConnectionRequestTimeOut: tiempo para obtener la conexión desde el grupo de conexión
Esta vez define el tiempo de tiempo de espera para recuperar la conexión del grupo de conexión administrado por ConnectionManager. Si no hay una conexión disponible en el grupo de conexión, la solicitud se bloqueará y el tiempo máximo para esperar ConnectionRequestTimeOut. Si no se ha servido, se lanza una excepción de ConexyPoolTimeOutException y no se permite más espera.
b. ConnectTimeOut: tiempo de tiempo de espera de la configuración
Esta vez define el tiempo de espera para establecer una conexión con el servidor a través de la red, es decir, el tiempo de espera de conexión para conectarse a la URL de destino después de obtener una conexión en el grupo de conexión. Se produce un tiempo de espera, se lanzará una excepción de conexión de conexión de conexión.
do. SocketTimeout: tiempo de tiempo de espera de la presupción
Esta vez define el tiempo de tiempo de espera para los datos de lectura de socket, es decir, el tiempo que lleva esperar después de conectarse al servidor para obtener datos de respuesta del servidor, o el tiempo que lleva obtener la respuesta después de conectarse a la URL anterior. Se produce un tiempo de espera y se lanzará una excepción de SocketTimeOutException.
6. Instanciar httpclient
El httpclient se instancia mediante la implementación de fábrica, el código es el siguiente:
importar org.apache.http.client.httprequestretryhandler; importar org.apache.http.client.config.requestconfig; importar org.apache.http.impl.conn.connectionkeepalivestrategy; importar org.apache.http.impl.client.ccloseablehttpclient; importar org.apache.http.impl.client.httpclients; importar org.apache.http.impl.conn.defaultProxyRoutePlanner; importar org.apache.http.impl.conn.poolinghttpClientConnectionManager; importar org.springframework.beans.factory.disposableBean; importar org.springframework.beans.factory.factorybean; importar org.springframework.beans.factory.initializingbean; importar org.springframework.beans.factory.annotation.aUtowired; importar org.springframework.stereotype.service; / *** Descripción: Encapsulación del cliente HttpClient*/ @Service ("httpClientManagerFactoryBen") clase pública httpClientManagerFactoryBen implementa factorybean <CloseableHttpClient>, inicializando el cable, el cliente de apourable {/ *** Objetivo generado por FactoryBeanBeanBeanHtttpclient Cliente; @AUTOWIREDEDIREDEDIR CONECTIÓN PRIVADO CONEXIPTY CONEXIMIENTOS @AUTOWIREDIRD PRIVADO HTTPREQUESTRYRYHANDLER HTTPREQUESTRINTRYHANDLER; @AUTOWIREDREDREDREDED PRIVAYPROXYROUTEPLANNER PROXYROUTEPLANNER; @AUTOWired private PoolinghttpClientConnectionManager Poolhttpcconnmanager; @AUTOWIREDIRD SELITCONFIG CONFIG; // Al destruir el contexto, destruir la instancia httpClient @Override public void destruye () lanza la excepción { / * * llamando a httpclient.close () cerrará primero el administrador de conexión, y luego liberará todos los recursos ocupados por el httpclient, * cierre todas las conexiones que están en uso o inactivos, incluido el socket subyacente. Dado que el administrador de conexión que usa está cerrado aquí, * Entonces, cuando tiene que hacer una solicitud HTTP la próxima vez, debe renovar un administrador de conexión para construir un httpclient, * es decir, cuando necesita cerrar y crear un nuevo cliente, el administrador de conexión no puede ser un singleton. */ if (null! = this.client) {this.client.close (); } } @Override// Initialize the instance public void afterPropertiesSet() throws Exception { /* * It is recommended to use HttpClients.custom to create HttpClientBuilder here, instead of using HttpClientBuilder.create() method to create HttpClientBuilder * From the official documentation, HttpClientBuilder is non-thread-safe, but HttpClients es realmente imutable. The immutable object can not only ensure that the state of the object is not changed, * but also can be shared by other threads without using the lock mechanism*/ this.client = HttpClients.custom().setConnectionManager(poolHttpcConnManager) .setRetryHandler(httpRequestRetryHandler) .setKeepAliveStrategy(connectionKeepAliveStrategy) .SetRoutePlanner (proxyRoutePlanner) .setDefaulTequestConfig (config) .Build (); } // Devuelve el tipo de instancia @Override public ClosableHttpClient getObject () lanza la excepción {return this.client; } @Override public class <?> GetObjectType () {return (this.client == NULL? ClosableHttpClient.Class: this.client.getclass ()); } // La instancia construida es un singleton@anular public boolean issingleton () {return true; }}7. Agregar archivos de configuración
# proxy host httpclient.config.proxyhost = xxx.xx.xx.xx # proxy puerto httpclient.config.proxyport = 8080 # tiempo de tiempo de conexión o excepción de retramado httpclient.config.retrytime = 3 # tiempo de conexión de larga duración, la unidad es s httpclient.config. wanthpclient en el grupo de conexión httpclient.config.connmaxToTal = 20 httpclient.config.maxperroute = 20 # tiempo de espera de conexión, unidad ms httpclient.config.connecttimtimeout = 20 # tiempo de tiempo de conexión, la unidad es ms httpclient.config.connecttimeOut = 20 # Solicitud de combinación httpclient.config.connectrectesttimeout = 2000 # Tiempo de espera de SOCK httpclient.config.socketTimeout = 2000 # Tiempo de supervivencia de conexión, unidad s httpclient.config.timetolive = 60
8. Prueba
El código de prueba es el siguiente:
import java.io.ioException; import java.util.concurrent.executorservice; import java.util.concurrent.executors; import javax.annotation.resource; importar org.apache.http.consts; importar org.apache.http.parseException; importar org.apache.http.client.clientprotocolexception; importar org.apache.http.client.methods.CloseableHttPResponse; importar org.apache.http.client.methods.httpget; importar org.apache.http.impl.client.ccloseablehttpclient; importar org.apache.http.util.entityutils; importar org.junit.test; importar org.junit.runner.runwith; importar org.springframework.boot.test.context.springboottest; importar org.springframework.test.context.junit4.springrunner; @RunWith (SpringRunner.class) @SpringBoottest Public Class HttpClientManagerFactoryBentest {// Inyect HttpClient @Test public void test () lanza ClientProtocolException, IOException, InterruptedException {EjecutorService Service = Ejecutors.NewFixedThreadPool (2); for (int i = 0; i <10; i ++) {Service.SubMit (new runnable () {@Override public void run () {System.out.println ("El hilo actual es:"+hilo.currentThread (). getName ()); httpentity entity = null; try {htttpget get = new New newname ()); Httpget ("https: // localhost: 8080/testjson"); System.out.println ("==================================================================================================================================================================================. Conexión} Catch (ClientProtocolException e) {E.PrintStackTrace (); }); A través de los pasos anteriores, la encapsulación de httpclient se completa básicamente. Si necesita ser más detallado, puede mejorar gradualmente el httpclient como httpclienttemplate de acuerdo con las ideas anteriores, porque ClosableHttpClient utiliza un mecanismo de devolución de llamada internamente, que es similar a JDBCTemplate o Redistemplate hasta que el servicio se pueda proporcionar en forma de arranque de arranque de primavera.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.