Estoy usando Spring RestTemplate para ejecutar la solicitud HTTP desde mi aplicación. Hay varios servicios a los que debemos llamar, algunos en Internet y otros en la intranet, algunos rápidos y otros lentos. Se me ha indicado que configure ajustes personalizados para cada servicio, básicamente, tiempo de espera de conexión, tiempo de espera de lectura.

Esta configuración será extremadamente específica, por ejemplo, los servicios alojados en la intranet tendrían un tiempo de espera de ~ 2-5 segundos mientras proporcionan un SLA de 1000ms para el 99.9% de las solicitudes. Mientras que los otros servicios de terceros con ~ 10-60s.

Como estos parámetros se pueden configurar en la fábrica solo para la plantilla, estoy creando una cantidad de beans con diferentes fábricas que difieren solo en los tiempos de espera. Algo como esto:

@Bean
RestTemplate restTemplate() {
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
    factory.setReadTimeout(20000);
    factory.setConnectTimeout(5000);
    RestTemplate restTemplate = new RestTemplate(factory);
}

Me temo que esto eventualmente creará una pesadilla de mantenimiento. ¿Se puede resolver de una mejor manera?

PD: La aplicación es un monolito que llama a varios servicios.

0
ashish.g 24 jun. 2020 a las 17:11

3 respuestas

La mejor respuesta

El uso de la construcción parametrizada del bean RestTemplate resolvió mi problema. El bean ahora está configurado como:

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public RestTemplate getRestTemplate(String configParam){
        int connectionTimeout; //get from config using configParam
        int readTimeout;  //get from config using configParam
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setConnectTimeout(connectionTimeout);
        factory.setReadTimeout(readTimeout);

        return new RestTemplate(factory);
    }

En lugar de @Autowiring este campo, se puede inyectar en el método @PostConstruct como se muestra a continuación. El bean dependiente puede hacer lo siguiente:

@Autowire
BeanFactory beanFactory;

RestTemplate restTemplate;

@PostConstruct
public void init(){
    restTemplate = beanFactory.getBean(RestTemplate.class, configParam);
}

Aquí puede inyectar su bean con configuraciones personalizadas en restTemplate.

0
ashish.g 25 jun. 2020 a las 12:46

Tendrá que crear múltiples RestTemplates y asignar tiempos de espera, tamaño de grupo de conexión. El grupo de conexiones mejorará drásticamente el rendimiento

He codificado las propiedades de conexión, puede elegirlo desde el archivo application.properties

@Configuration
class RestTemplateConfigs {
    @Bean
    public HttpClient httpClient() {
        return HttpClientBuilder.create()
                .setMaxConnPerRoute(200)
                .setMaxConnTotal(50)
                .setConnectionTimeToLive(10L, TimeUnit.SECONDS)
                .build();
    }

    @Bean(name = "restTemplate1")
    RestTemplate restTemplate() {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient());
        RestTemplate restTemplate = new RestTemplate(factory);
        return restTemplate;
    }
}

Puede crear múltiples RestTemplates y Autowire usando el nombre de Qualifier.

1
Govinda Sakhare 24 jun. 2020 a las 15:15

Descargo de responsabilidad : mi respuesta sugiere trabajar con otro cliente Http que no sea Plantilla de descanso: si debe usar la plantilla de descanso, mi respuesta sería irrelevante.

Me ocupé de un problema de diseño similar y esto es lo que hice. Escribí mi propia clase HttpClient. Es mucho más simple de usar que los clientes Http más conocidos. Esta clase podría usarse por sí sola o (y esto es relevante para su caso) podría usarse como una clase principal para un grupo de clases (implementando la misma interfaz) donde cada clase será un cliente Http para un Servicio de descanso específico. En esta clase, puede preestablecer la URL de destino y todos los parámetros (como los tiempos de espera de lectura y conexión, etc.). Una vez que esta clase está preestablecida, todo lo que necesita hacer es invocar sendHttpRequestMethod (). Solo para expandir un poco, supongamos que tiene un servicio de descanso de usuario con API CRUD implementado por llamadas URL particulares con diferentes métodos HTTP y puede ser diferentes URL. (digamos, además de crear (POST) actualizar (PUT) leer (OBTENER) y eliminar (ELIMINAR) métodos que se encuentran en HTTP://www.myserver.com:8083/user dice que también tendrá métodos para activar y desactivar ( diga ambos GET) en las URL HTTP://www.myserver.com:8083/user/activate/ y HTTP://www.myserver.com:8083/user/deactivate.Entonces, en este caso, su cliente Http establecerá todos los tiempos de espera requeridos y otras configuraciones y también tendrá una URL de destino preestablecida HTTP://www.myserver.com:8083/user. y tendrá seis métodos como se mencionó anteriormente, donde cada uno simplemente invocará el método de clase padre sendHttpRequest (). Por supuesto, para activar y desactivar métodos, deberá agregar los sufijos "activar" y "desactivar" para la URL base preestablecida. Por lo tanto, para cada servicio REST, puede crear un cliente Http dedicado con un esfuerzo mínimo ya que la clase base ya hace la mayor parte del trabajo. Además de eso, escribí una fábrica autopoblada para cualquier grupo de clases que implementen la misma interfaz. Con esa fábrica, todo lo que tendrá que hacer es escribir su cliente Http adicional y la fábrica lo detectará y lo pondrá a disposición por su nombre predefinido o por el nombre de la clase (según su elección). Todo esto funcionó tan bien para mí, que lo empaqueté en una biblioteca de código abierto llamada MgntUtils y lo publiqué en Maven y Github (con fuente código y Javadoc. JavaDoc está disponible aquí). Se puede ver una explicación detallada sobre la fábrica autopoblada en Javadoc aquí. Además, el artículo general sobre la biblioteca se puede encontrar aquí y un artículo específico sobre la idea y la implementación de Factory autopoblada se puede encontrar aquí. El paquete com.mgnt.lifecycle.management.example en el código fuente contiene un ejemplo de trabajo. Espero que esto ayude.

0
Michael Gantman 24 jun. 2020 a las 15:00