Comprensión profunda del grupo de conexiones HttpClient

Original: https://www.jb51.net/article/141015.htm

Tabla de contenido

1. Antecedentes

2. Keep-Alive de HTTP/1.0+

3. Conexión persistente de HTTP/1.1

4. ¿Cómo genera HttpClient una conexión persistente?

4.1 Implementación del grupo de conexiones HttpClient

4.2 Futuro

4.3 Conexión HttpClient

5. ¿Cómo reutiliza HttpClient las conexiones persistentes?

 6. ¿Cómo limpia HttpClient las conexiones caducadas?

7. Resumen de este artículo

Ocho, configuración importante del grupo de subprocesos httpcleint


1. Antecedentes

El protocolo HTTP es un protocolo sin estado, es decir, cada solicitud es independiente entre sí. Por lo tanto, su implementación inicial es que cada solicitud http abrirá una conexión de socket tcp y la conexión se cerrará cuando se complete la interacción.

El protocolo HTTP es un protocolo dúplex completo, por lo que establecer y desconectar requiere tres apretones de manos y cuatro manos agitadas. Obviamente, en este diseño, cada vez que se envía una solicitud Http, se consumirán muchos recursos adicionales, es decir, el establecimiento y la destrucción de la conexión.

Por lo tanto, también se ha desarrollado el protocolo HTTP y la multiplexación de la conexión de socket se realiza a través del método de conexión persistente.

Como se puede ver en la figura:

  • En una conexión serial, cada interacción abre y cierra la conexión
  • En una conexión persistente, la primera interacción abrirá la conexión y la conexión no se cerrará después de la interacción. La siguiente interacción guardará el proceso de establecimiento de una conexión.

Hay dos implementaciones de conexiones persistentes: conexiones persistentes HTTP/1.0+ y HTTP/1.1.

2. Keep-Alive de HTTP/1.0+

Desde 1996, muchos navegadores y servidores HTTP/1.0 han ampliado el protocolo, que es el protocolo de extensión "keep-alive".

Tenga en cuenta que este protocolo de extensión aparece como un suplemento de "conexión persistente experimental" para 1.0. keep-alive ya no se usa y no se explica en la especificación HTTP/1.1 más reciente, pero muchas aplicaciones continúan.

Los clientes que usan HTTP/1.0 agregan "Conexión: Keep-Alive" al encabezado, solicitando al servidor que mantenga una conexión abierta. El servidor incluirá el mismo encabezado en la respuesta si desea mantener abierta la conexión. Si la respuesta no contiene el encabezado "Conexión: Keep-Alive", el cliente pensará que el servidor no es compatible con Keep-Alive y cerrará la conexión actual después de enviar el mensaje de respuesta.

A través del protocolo complementario keep-alive, se completa la conexión persistente entre el cliente y el servidor, pero todavía hay algunos problemas:

  • Keep-alive no es un protocolo estándar en HTTP/1.0 y el cliente debe enviar Connection:Keep-Alive para activar la conexión keep-alive.
  • Es posible que los servidores proxy no admitan keep-alive, porque algunos proxies son "transmisores ciegos", incapaces de comprender el significado del encabezado y simplemente reenvían el encabezado salto por salto. Por lo tanto, puede provocar la conexión entre el cliente y el servidor, pero el proxy no acepta los datos de la conexión.

3. Conexión persistente de HTTP/1.1

HTTP/1.1 reemplaza Keep-Alive con una conexión persistente.

Las conexiones HTTP/1.1 son persistentes de forma predeterminada. Si desea cerrarlo explícitamente, debe agregar el encabezado Connection:Close al mensaje. Es decir, en HTTP/1.1, todas las conexiones están multiplexadas.

Sin embargo, al igual que Keep-Alive, el cliente y el servidor también pueden cerrar las conexiones persistentes inactivas en cualquier momento. No enviar Connection:Close no significa que el servidor prometa mantener la conexión abierta para siempre.

4. ¿Cómo genera HttpClient una conexión persistente?

HttpClien usa un grupo de conexiones para administrar las conexiones retenidas y las conexiones se pueden reutilizar en el mismo enlace TCP. HttpClient implementa la persistencia de la conexión a través de la agrupación de conexiones.

De hecho, la tecnología de "piscina" es un diseño general y su idea de diseño no es complicada:

  • Establecer una conexión cuando se utiliza una conexión por primera vez
  • Al final, la conexión correspondiente no se cierra y se devuelve al grupo.
  • La próxima conexión para el mismo propósito puede obtener una conexión disponible del grupo
  • Limpiar periódicamente las conexiones caducadas

Todos los grupos de conexiones se basan en esta idea, pero cuando observamos el código fuente de HttpClient, nos enfocamos principalmente en dos puntos:

  • El esquema de diseño específico del grupo de conexiones, como referencia para personalizar el grupo de conexiones en el futuro
  • Cómo corresponder al protocolo HTTP, es decir, la realización de la abstracción teórica en código

4.1 Implementación del grupo de conexiones HttpClient

El manejo de conexiones persistentes por parte de HttpClient se puede concentrar en el siguiente código. La parte relacionada con el conjunto de conexiones se extrae de MainClientExec y se eliminan otras partes:

public class MainClientExec implements ClientExecChain {

 @Override
 public CloseableHttpResponse execute(
  final HttpRoute route,
  final HttpRequestWrapper request,
  final HttpClientContext context,
  final HttpExecutionAware execAware) throws IOException, HttpException {
     //从连接管理器HttpClientConnectionManager中获取一个连接请求ConnectionRequest
 final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);final HttpClientConnection managedConn;
 final int timeout = config.getConnectionRequestTimeout(); //从连接请求ConnectionRequest中获取一个被管理的连接HttpClientConnection
 managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
     //将连接管理器HttpClientConnectionManager与被管理的连接HttpClientConnection交给一个ConnectionHolder持有
 final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
 try {
  HttpResponse response;
  if (!managedConn.isOpen()) {          //如果当前被管理的连接不是出于打开状态,需要重新建立连接
  establishRoute(proxyAuthState, managedConn, route, request, context);
  }
       //通过连接HttpClientConnection发送请求
  response = requestExecutor.execute(request, managedConn, context);
       //通过连接重用策略判断是否连接可重用  
  if (reuseStrategy.keepAlive(response, context)) {
  //获得连接有效期
  final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
  //设置连接有效期
  connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);          //将当前连接标记为可重用状态
  connHolder.markReusable();
  } else {
  connHolder.markNonReusable();
  }
 }
 final HttpEntity entity = response.getEntity();
 if (entity == null || !entity.isStreaming()) {
  //将当前连接释放到池中,供下次调用
  connHolder.releaseConnection();
  return new HttpResponseProxy(response, null);
 } else {
  return new HttpResponseProxy(response, connHolder);
 }
}

Aquí vemos que el procesamiento de la conexión en el proceso de solicitud Http es consistente con la especificación del protocolo, y aquí ampliaremos la implementación específica.

PoolingHttpClientConnectionManager es el administrador de conexión predeterminado de HttpClient. Primero, obtenga una solicitud de conexión a través de requestConnection(). Tenga en cuenta que esto no es una conexión.

public ConnectionRequest requestConnection(
  final HttpRoute route,
  final Object state) {final Future<CPoolEntry> future = this.pool.lease(route, state, null);
 return new ConnectionRequest() {
  @Override
  public boolean cancel() {
  return future.cancel(true);
  }
  @Override
  public HttpClientConnection get(
   final long timeout,
   final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException {
  final HttpClientConnection conn = leaseConnection(future, timeout, tunit);
  if (conn.isOpen()) {
   final HttpHost host;
   if (route.getProxyHost() != null) {
   host = route.getProxyHost();
   } else {
   host = route.getTargetHost();
   }
   final SocketConfig socketConfig = resolveSocketConfig(host);
   conn.setSocketTimeout(socketConfig.getSoTimeout());
  }
  return conn;
  }
 };
 }

Se puede ver que el objeto ConnectionRequest devuelto es en realidad un Future<CPoolEntry> de retención, y CPoolEntry es la instancia de conexión real administrada por el grupo de conexiones.

Del código anterior debemos centrarnos en:

Future<CPoolEntry> future = this.pool.lease(route, state, null)

  Cómo obtener una conexión asíncrona desde el grupo de conexiones CPool, Future<CPoolEntry>

HttpClientConnection conn = leaseConnection(future, timeout, tunit)

  Cómo obtener una conexión real HttpClientConnection a través de la conexión asíncrona Future<CPoolEntry>

4.2 Futuro<CPoolEntry>

Eche un vistazo a cómo CPool lanza un Future<CPoolEntry>, el código central de AbstractConnPool es el siguiente:

private E getPoolEntryBlocking(
  final T route, final Object state,
  final long timeout, final TimeUnit tunit,
  final Future<E> future) throws IOException, InterruptedException, TimeoutException {
     //首先对当前连接池加锁,当前锁是可重入锁ReentrantLockthis.lock.lock();
 try {        //获得一个当前HttpRoute对应的连接池,对于HttpClient的连接池而言,总池有个大小,每个route对应的连接也是个池,所以是“池中池”
  final RouteSpecificPool<T, C, E> pool = getPool(route);
  E entry;
  for (;;) {
  Asserts.check(!this.isShutDown, "Connection pool shut down");          //死循环获得连接
  for (;;) {            //从route对应的池中拿连接,可能是null,也可能是有效连接
   entry = pool.getFree(state);            //如果拿到null,就退出循环
   if (entry == null) {
   break;
   }            //如果拿到过期连接或者已关闭连接,就释放资源,继续循环获取
   if (entry.isExpired(System.currentTimeMillis())) {
   entry.close();
   }
   if (entry.isClosed()) {
   this.available.remove(entry);
   pool.free(entry, false);
   } else {              //如果拿到有效连接就退出循环
   break;
   }
  }          //拿到有效连接就退出
  if (entry != null) {
   this.available.remove(entry);
   this.leased.add(entry);
   onReuse(entry);
   return entry;
  }
          //到这里证明没有拿到有效连接,需要自己生成一个  
  final int maxPerRoute = getMax(route);
  //每个route对应的连接最大数量是可配置的,如果超过了,就需要通过LRU清理掉一些连接
  final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
  if (excess > 0) {
   for (int i = 0; i < excess; i++) {
   final E lastUsed = pool.getLastUsed();
   if (lastUsed == null) {
    break;
   }
   lastUsed.close();
   this.available.remove(lastUsed);
   pool.remove(lastUsed);
   }
  }
          //当前route池中的连接数,没有达到上线
  if (pool.getAllocatedCount() < maxPerRoute) {
   final int totalUsed = this.leased.size();
   final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);            //判断连接池是否超过上线,如果超过了,需要通过LRU清理掉一些连接
   if (freeCapacity > 0) {
   final int totalAvailable = this.available.size();               //如果空闲连接数已经大于剩余可用空间,则需要清理下空闲连接
   if (totalAvailable > freeCapacity - 1) {
    if (!this.available.isEmpty()) {
    final E lastUsed = this.available.removeLast();
    lastUsed.close();
    final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
    otherpool.remove(lastUsed);
    }
   }              //根据route建立一个连接
   final C conn = this.connFactory.create(route);              //将这个连接放入route对应的“小池”中
   entry = pool.add(conn);              //将这个连接放入“大池”中
   this.leased.add(entry);
   return entry;
   }
  }
         //到这里证明没有从获得route池中获得有效连接,并且想要自己建立连接时当前route连接池已经到达最大值,即已经有连接在使用,但是对当前线程不可用
  boolean success = false;
  try {
   if (future.isCancelled()) {
   throw new InterruptedException("Operation interrupted");
   }            //将future放入route池中等待
   pool.queue(future);            //将future放入大连接池中等待
   this.pending.add(future);            //如果等待到了信号量的通知,success为true
   if (deadline != null) {
   success = this.condition.awaitUntil(deadline);
   } else {
   this.condition.await();
   success = true;
   }
   if (future.isCancelled()) {
   throw new InterruptedException("Operation interrupted");
   }
  } finally {
   //从等待队列中移除
   pool.unqueue(future);
   this.pending.remove(future);
  }
  //如果没有等到信号量通知并且当前时间已经超时,则退出循环
  if (!success && (deadline != null && deadline.getTime() <= System.currentTimeMillis())) {
   break;
  }
  }       //最终也没有等到信号量通知,没有拿到可用连接,则抛异常
  throw new TimeoutException("Timeout waiting for connection");
 } finally {       //释放对大连接池的锁
  this.lock.unlock();
 }
 }

Hay varios puntos importantes en la lógica del código anterior:

  • El grupo de conexiones tiene un número máximo de conexiones, cada ruta corresponde a un grupo de conexiones pequeño y también hay un número máximo de conexiones
  • Ya sea que se trate de un grupo de conexiones grande o un grupo de conexiones pequeño, cuando el número excede, algunas conexiones deben liberarse a través de LRU
  • Si obtiene una conexión disponible, devuélvala a la capa superior para su uso
  • Si no se obtiene una conexión disponible, HttpClient juzgará si el grupo de conexiones de ruta actual ha excedido el número máximo y, si no se alcanza el límite superior, se creará una nueva conexión y se colocará en el grupo.
  • Si se alcanza el límite superior, espere en línea, espere el semáforo, obténgalo nuevamente y genere una excepción de tiempo de espera si la espera es menor que
  • La obtención de una conexión a través del grupo de subprocesos debe bloquearse a través de ReetrantLock para garantizar la seguridad de los subprocesos.

Hasta ahora, el programa ha obtenido una instancia de CPoolEntry disponible o el programa finaliza al generar una excepción.

4.3 Conexión HttpClient

protected HttpClientConnection leaseConnection(
  final Future<CPoolEntry> future,
  final long timeout,
  final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException {
 final CPoolEntry entry;
 try {       //从异步操作Future<CPoolEntry>中获得CPoolEntry
  entry = future.get(timeout, tunit);
  if (entry == null || future.isCancelled()) {
  throw new InterruptedException();
  }
  Asserts.check(entry.getConnection() != null, "Pool entry with no connection");
  if (this.log.isDebugEnabled()) {
  this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
  }       //获得一个CPoolEntry的代理对象,对其操作都是使用同一个底层的HttpClientConnection
  return CPoolProxy.newProxy(entry);
 } catch (final TimeoutException ex) {
  throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool");
 }
 }

5. ¿Cómo reutiliza HttpClient las conexiones persistentes?

En el capítulo anterior, vimos que HttpClient obtiene una conexión a través de un grupo de conexiones y la obtiene del grupo cuando necesita usar una conexión.

Correspondientes a las preguntas del Capítulo 3:

  • Establecer una conexión cuando se utiliza una conexión por primera vez
  • Al final, la conexión correspondiente no se cierra y se devuelve al grupo.
  • La próxima conexión para el mismo propósito puede obtener una conexión disponible del grupo
  • Limpiar periódicamente las conexiones caducadas

Vimos en el Capítulo 4 cómo HttpClient maneja los problemas 1 y 3, entonces, ¿cómo maneja el segundo problema?

Es decir, ¿cómo determina HttpClient si una conexión debe cerrarse después de su uso o si debe colocarse en el grupo para que otros la reutilicen? Mire el código de MainClientExec nuevamente

//发送Http连接  response = requestExecutor.execute(request, managedConn, context);
  //根据重用策略判断当前连接是否要复用
  if (reuseStrategy.keepAlive(response, context)) {
   //需要复用的连接,获取连接超时时间,以response中的timeout为准
   final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
   if (this.log.isDebugEnabled()) {
   final String s;               //timeout的是毫秒数,如果没有设置则为-1,即没有超时时间
   if (duration > 0) {
    s = "for " + duration + " " + TimeUnit.MILLISECONDS;
   } else {
    s = "indefinitely";
   }
   this.log.debug("Connection can be kept alive " + s);
   }            //设置超时时间,当请求结束时连接管理器会根据超时时间决定是关闭还是放回到池中
   connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
   //将连接标记为可重用            connHolder.markReusable();
  } else {            //将连接标记为不可重用
   connHolder.markNonReusable();
  }

Se puede ver que cuando se produce una solicitud mediante una conexión, existe una estrategia de reintento de conexión para determinar si se debe reutilizar la conexión. Si se va a reutilizar, se entregará a HttpClientConnectionManager y se colocará en el grupo después del final. .

Entonces, ¿cuál es la lógica de la estrategia de reutilización de conexiones?

public class DefaultClientConnectionReuseStrategy extends DefaultConnectionReuseStrategy {

 public static final DefaultClientConnectionReuseStrategy INSTANCE = new DefaultClientConnectionReuseStrategy();

 @Override
 public boolean keepAlive(final HttpResponse response, final HttpContext context) {
     //从上下文中拿到request
  final HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST);
  if (request != null) {       //获得Connection的Header
   final Header[] connHeaders = request.getHeaders(HttpHeaders.CONNECTION);
   if (connHeaders.length != 0) {
    final TokenIterator ti = new BasicTokenIterator(new BasicHeaderIterator(connHeaders, null));
    while (ti.hasNext()) {
     final String token = ti.nextToken();            //如果包含Connection:Close首部,则代表请求不打算保持连接,会忽略response的意愿,该头部这是HTTP/1.1的规范
     if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
      return false;
     }
    }
   }
  }     //使用父类的的复用策略
  return super.keepAlive(response, context);
 }
}

Mire la estrategia de reutilización de la clase padre.

if (canResponseHaveBody(request, response)) {
    final Header[] clhs = response.getHeaders(HTTP.CONTENT_LEN);
    //如果reponse的Content-Length没有正确设置,则不复用连接          //因为对于持久化连接,两次传输之间不需要重新建立连接,则需要根据Content-Length确认内容属于哪次请求,以正确处理“粘包”现象    //所以,没有正确设置Content-Length的response连接不能复用
    if (clhs.length == 1) {
     final Header clh = clhs[0];
     try {
      final int contentLen = Integer.parseInt(clh.getValue());
      if (contentLen < 0) {
       return false;
      }
     } catch (final NumberFormatException ex) {
      return false;
     }
    } else {
     return false;
    }
   }
  if (headerIterator.hasNext()) {
   try {
    final TokenIterator ti = new BasicTokenIterator(headerIterator);
    boolean keepalive = false;
    while (ti.hasNext()) {
     final String token = ti.nextToken();            //如果response有Connection:Close首部,则明确表示要关闭,则不复用
     if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
      return false;            //如果response有Connection:Keep-Alive首部,则明确表示要持久化,则复用
     } else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(token)) {
      keepalive = true;
     }
    }
    if (keepalive) {
     return true;
    }
   } catch (final ParseException px) {
    return false;
   }
  }
     //如果response中没有相关的Connection首部说明,则高于HTTP/1.0版本的都复用连接 
  return !ver.lessEquals(HttpVersion.HTTP_1_0);

en conclusión:

  • Si el encabezado de la solicitud contiene Connection:Close, no se reutilizará
  • Si Content-Length en la respuesta se configura incorrectamente, no se reutilizará
  • Si el encabezado de respuesta contiene Connection:Close, no se reutilizará
  • Si el encabezado de respuesta contiene Conexión: Keep-Alive, reutilice
  • En caso de no acierto, si la versión de HTTP es superior a 1.0, se reutilizará

Como se puede ver en el código, su estrategia de implementación es consistente con las restricciones de nuestra capa de protocolo en los Capítulos 2 y 3.

 6. ¿Cómo limpia HttpClient las conexiones caducadas?

Antes de la versión HttpClient4.4, al reutilizar la conexión del grupo de conexiones, comprobará si ha caducado y se limpiará cuando caduque.

La última versión es diferente, habrá un subproceso separado para escanear la conexión en el grupo de conexiones, y se limpiará cuando encuentre que el tiempo transcurrido desde el último uso excede el tiempo establecido. El tiempo de espera predeterminado es de 2 segundos.

public CloseableHttpClient build() {   //如果指定了要清理过期连接与空闲连接,才会启动清理线程,默认是不启动的
   if (evictExpiredConnections || evictIdleConnections) {          //创造一个连接池的清理线程
    final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
      maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS,
      maxIdleTime, maxIdleTimeUnit);
    closeablesCopy.add(new Closeable() {
     @Override
     public void close() throws IOException {
      connectionEvictor.shutdown();
      try {
       connectionEvictor.awaitTermination(1L, TimeUnit.SECONDS);
      } catch (final InterruptedException interrupted) {
       Thread.currentThread().interrupt();
      }
     }

    });          //执行该清理线程
    connectionEvictor.start();
}

Se puede ver que cuando HttpClientBuilder se está construyendo, si se especifica la función de limpieza, se creará y ejecutará un subproceso de limpieza del grupo de conexiones.

public IdleConnectionEvictor(
   final HttpClientConnectionManager connectionManager,
   final ThreadFactory threadFactory,
   final long sleepTime, final TimeUnit sleepTimeUnit,
   final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
  this.connectionManager = Args.notNull(connectionManager, "Connection manager");
  this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory();
  this.sleepTimeMs = sleepTimeUnit != null ? sleepTimeUnit.toMillis(sleepTime) : sleepTime;
  this.maxIdleTimeMs = maxIdleTimeUnit != null ? maxIdleTimeUnit.toMillis(maxIdleTime) : maxIdleTime;
  this.thread = this.threadFactory.newThread(new Runnable() {
   @Override
   public void run() {
    try {            //死循环,线程一直执行
     while (!Thread.currentThread().isInterrupted()) {              //休息若干秒后执行,默认10秒
      Thread.sleep(sleepTimeMs);               //清理过期连接
      connectionManager.closeExpiredConnections();               //如果指定了最大空闲时间,则清理空闲连接
      if (maxIdleTimeMs > 0) {
       connectionManager.closeIdleConnections(maxIdleTimeMs, TimeUnit.MILLISECONDS);
      }
     }
    } catch (final Exception ex) {
     exception = ex;
    }

   }
  });
 }

en conclusión:

  • Solo después de configurar manualmente HttpClientBuilder, se habilitará para limpiar las conexiones caducadas e inactivas
  • Después de la configuración manual, iniciará un subproceso para ejecutar en un bucle sin fin. Cada vez que ejecute el modo de suspensión durante un cierto período de tiempo, llamará al método de limpieza de HttpClientConnectionManager para limpiar las conexiones caducadas e inactivas.

7. Resumen de este artículo

  • El protocolo HTTP alivia el problema de demasiadas conexiones en el diseño inicial por medio de conexiones persistentes
  • Hay dos formas de conexión persistente: Keep-Avlive de HTTP/1.0+ y la conexión persistente predeterminada de HTTP/1.1
  • HttpClient administra las conexiones persistentes a través del grupo de conexiones, el grupo de conexiones se divide en dos, uno es el grupo de conexiones total y el otro es el grupo de conexiones correspondiente a cada ruta.
  • HttpClient obtiene una conexión agrupada a través de Future<CPoolEntry> asíncrono
  • La estrategia de reutilización de conexión predeterminada es coherente con las restricciones del protocolo HTTP. De acuerdo con la respuesta, primero se considera que Connection:Close está cerrado, y Connection:Keep-Alive se considera habilitado, y la última versión es mayor. que 1.0 para ser habilitado.
  • Las conexiones en el grupo de conexiones solo se limpiarán si el interruptor para limpiar las conexiones caducadas e inactivas se habilita manualmente en HttpClientBuilder
  • Las versiones posteriores a HttpClient 4.4 limpian las conexiones caducadas e inactivas a través de un subproceso de bucle infinito, que duerme durante un tiempo cada vez que se ejecuta, para lograr el efecto de una ejecución regular

Ocho, configuración importante del grupo de subprocesos httpcleint

        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create().register("http", plainsf).register("https", getSslFactory()).build();
        //创建连接池管理器
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        // 最大连接数
        cm.setMaxTotal(maxTotal);
        // 默认的每个路由的最大连接数(也就是相同主机最大连接数)
        cm.setDefaultMaxPerRoute(maxPerRoute);
        //        HttpHost httpHost = new HttpHost(hostname, port);
        //        // 设置到某个路由的最大连接数,会覆盖defaultMaxPerRoute
        //        cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute);

setMaxTotal y setDefaultMaxPerRoute

 

 

 

Supongo que te gusta

Origin blog.csdn.net/lan861698789/article/details/112975083
Recomendado
Clasificación