En aplicaciones Spring Cloud que utilizan Feign para realizar llamadas a otros microservicios, es frecuente experimentar inestabilidad, donde las peticiones fallan de manera intermitente. Un eror típico que puede observarse es una excepción de socket relacionada con la conexión:
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
... (otros marcos de pila)
Este fallo suele deberse a limitaciones en la configuración del cliente HTTP subyacente, como la falta de un pool de conexiones adecuado o tiempos de espera no optimizados.
Para mitigar la inestabilidad, se puede sobrescribir la configuración predeterminada de HttpClient en Feign. A continuación, se muestra una clase de configuración que define un pool de conexiones con parámetros ajustados:
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.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.NoConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@Component
public class HttpClientConfig {
private static final int MAX_TOTAL_CONNECTIONS = 3000;
private static final int MAX_CONNECTIONS_PER_ROUTE = 500;
private static final int INACTIVITY_CHECK_MS = 1000;
@Bean(name = "httpClient", destroyMethod = "close")
public CloseableHttpClient createHttpClient() throws KeyManagementException {
return initializeHttpClient();
}
private CloseableHttpClient initializeHttpClient() throws KeyManagementException {
SSLContext context = SSLContexts.createDefault();
context.init(null, new TrustManager[]{new UniversalTrustManager()}, null);
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(context, NoopHostnameVerifier.INSTANCE);
Registry<connectionsocketfactory> registry = RegistryBuilder.<connectionsocketfactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry);
manager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
manager.setDefaultMaxPerRoute(MAX_CONNECTIONS_PER_ROUTE);
manager.setValidateAfterInactivity(INACTIVITY_CHECK_MS);
return HttpClients.custom()
.setConnectionManager(manager)
.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE)
.build();
}
private static class UniversalTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
}</connectionsocketfactory></connectionsocketfactory>
Esta configuración incrementa el número máximo de conexiones, establece un límite por ruta y habilita la verificación de inactividad para mantener el pool saludable.
Feign integra Ribbon y Hystrix, lo que puede causar timeouts rápidos si no se configuran adecuadamente. Para evitar errores de tiempo de espera, es neecsario ajustar varios parámetros.
Primero, configura los tiempos de conexión y lectura para el cliente Feign. Reemplaza nombreDelServicio con el nombre del microsrevicio objetivo:
feign.client.config.nombreDelServicio.connectTimeout=60000
feign.client.config.nombreDelServicio.readTimeout=60000
Desactivar Hystrix puede ser útil si se prefiere un manejo de errores más directo, aunque se pierden las capacidades de circuit breaker:
feign.hystrix.enabled=false
Además, si Hystrix está habilitado, su tiempo de timeout debe ser mayor o igual al de Feign; de lo contrario, las configuraciones de Feign no tendrán efecto:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000
Para configuraciones más granulares, se pueden establecer tiempos de espera específicos mediante Ribbon. Ejemplo global:
ribbon.ConnectTimeout=1000
ribbon.ReadTimeout=1000
O por servicio, con reintentos habilitados:
nombreDelServicio.ribbon.ConnectTimeout=10000
nombreDelServicio.ribbon.ReadTimeout=10000
nombreDelServicio.ribbon.OkToRetryOnAllOperations=true
nombreDelServicio.ribbon.MaxAutoRetriesNextServer=2
nombreDelServicio.ribbon.MaxAutoRetries=1
Estos ajustes permiten mayor resiliencia en las llamadas, manejando fallos transitorios mediante reintentos y tiempos de espera extendidos.