tutorial restful responseentity postmapping example espaƱol ejemplo crear con java spring rest http apache-httpcomponents

java - restful - Usando la plantilla Spring REST, ya sea creando demasiadas conexiones o lento



spring rest json (1)

Tengo un servicio RESTful que funciona muy rápido. Lo estoy probando en localhost. El cliente está utilizando la plantilla Spring REST. Comencé utilizando un enfoque ingenuo:

RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter())); Result result = restTemplate.postForObject(url, payload, Result.class);

Cuando hago muchas de estas solicitudes, obtengo la siguiente excepción:

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/myservice":No buffer space available (maximum connections reached?): connect; nested exception is java.net.SocketException: No buffer space available (maximum connections reached?): connect

Esto se debe a que las conexiones no se cierran y se cuelgan en el estado TIME_WAIT. La excepción comienza a ocurrir cuando los puertos efímeros se agotan. Luego, la ejecución espera a que los puertos vuelvan a estar libres. Estoy viendo el máximo rendimiento con largos descansos. La tarifa que estoy obteniendo es casi la que necesito, pero, por supuesto, estas conexiones TIME_WAIT no son buenas. Probado tanto en Linux (Ubuntu 14) como en Windows (7), resultados similares en diferentes momentos debido a diferentes rangos de puertos.

Para solucionar esto, intenté usar un HttpClient con HttpClientBuilder de la biblioteca de componentes de Apache Http.

RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter())); HttpClient httpClient = HttpClientBuilder.create() .setMaxConnTotal(TOTAL) .setMaxConnPerRoute(PER_ROUTE) .build(); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); Result result = restTemplate.postForObject(url, payload, Result.class);

Con este cliente, no veo excepciones. El cliente ahora está usando solo un número muy limitado de puertos efímeros. Pero sea cual sea la configuración que use (TOTAL y PER_ROUTE), no puedo obtener el rendimiento que necesito.

Al usar el comando netstat , veo que no hay muchas conexiones hechas al servidor. Intenté establecer los números en varios miles, pero parece que el cliente nunca usa tanto.

¿Hay algo que pueda hacer para mejorar el rendimiento sin abrir demasiadas conexiones?

ACTUALIZACIÓN: he intentado establecer el número de conexiones totales y por ruta a 5000 y 2500, pero aún parece que el cliente no está creando más de cien (a juzgar por netstat -n | wc -l ). El servicio REST se implementa utilizando JAX-RS y ejecutándose en Jetty.

ACTUALIZACIÓN2: Ahora he sintonizado el servidor con algunas configuraciones de memoria y estoy obteniendo un rendimiento realmente bueno. El enfoque ingenuo es todavía un poco más rápido, pero creo que es solo una pequeña sobrecarga de la agrupación en el lado del cliente.


En realidad Spring Boot no está perdiendo conexiones. Lo que está viendo aquí es el comportamiento estándar del kernel de Linux (y de todos los sistemas operativos principales). Todas las tomas que están cerradas desde la máquina pasan a un estado TIME_WAIT durante algún tiempo. Esto es para evitar que el próximo socket que usa ese puerto efímero reciba paquetes que realmente fueron destinados para el socket anterior en ese puerto. La diferencia que está viendo entre los dos es el resultado de los enfoques de agrupación de conexiones que cada uno toma.

Más específicamente, RestTemplate no usa la agrupación de conexiones de forma predeterminada. Esto significa que cada llamada de descanso abre un nuevo puerto efímero local y una nueva conexión al servidor. Si su servicio es muy rápido, pasará rápidamente a través de su rango de puerto local disponible. Con HttpClient , está aprovechando la agrupación de conexiones. Esto evitará que su aplicación vea el problema que describió. Sin embargo, dado que su servicio es capaz de responder más rápido de lo que el kernel de Linux está sacando sockets de TIME_WAIT , la agrupación de conexiones hará que su cliente sea más lento sin importar lo que haga (si no retrasó nada, entonces se agotaría) de puertos efímeros locales de nuevo).

Si bien es posible habilitar la reutilización de TCP en el kernel de Linux, puede ser peligroso (los paquetes se pueden retrasar y usted podría obtener puertos efímeros que reciben paquetes aleatorios que no entienden, lo que podría causar todo tipo de problemas). La solución aquí es usar la agrupación de conexiones como lo ha hecho en el segundo ejemplo, con números suficientemente altos para lograr el rendimiento que está buscando.

Para ayudarlo a ajustar su grupo de conexiones, deberá ajustar los parámetros maxConnPerRoute y maxConnTotal . maxConnPerRoute limita la cantidad de conexiones que se realizarán a una única IP: par de puertos, y maxTotal limita la cantidad de conexiones totales que se abrirán. En su caso, como parece que todas las solicitudes se realizan en la misma ubicación, puede establecerlas en el mismo valor (alto).