简体   繁体   中英

How to configure and build a custom RestTemplate for each client in Spring?

I am using Spring RestTemplate for executing HTTP request from my application. There are several services we need to call, some on the internet and some on intranet, some fast and some slow. I have been instructed to configure custom settings for each service, basically, connection timeout, read timeout.

These setting will be extremely specific for example, the services hosted on intranet would have a timeout of ~2-5s while they provide an SLA of 1000ms for 99.9% of requests. While the other third party services with ~10-60s.

As these parameters can be set in the factory for the template only, i am creating a number of beans with different factories differing in timeouts only. Something like this:

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

I am afraid this will create a maintenance nightmare eventually. Can it be solved in a better ways?

PS: The application is a monolith calling various services.

You will have to create multiple RestTemplates and assign timeouts, connection pool size. Connection pool will improve the performance drastically

I have hard-coded the connection properties, you can pick it from application.properties file

@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;
    }
}

You can create multiple RestTemplates and Autowire it using Qualifier name.

Disclaimer : my answer suggests to work with another Http client than Rest Template - If you must use Rest template my answer would be irrelevant.

I dealt with a similar design issue and here is what I did. I wrote my own HttpClient class. It is much simpler in use then most known Http clients. This class could be used on its own or (and this is relevant for your case) it could be used as a parent class for a group of classes (implementing the same interface) where each class will be an Http client for specific Rest Service. On this class you can pre-set the target URL and all the parameters (such as read and connection timeouts etc). Once this class is preset, all you need to do is to invoke sendHttpRequestMethod(). Just to expand a bit - lets say you have a User Rest service with CRUD API implemented by particular URL calls with different HTTP methods and may be different URLs. (say in addition to create (POST) update (PUT) read (GET) and delete (DELETE) methods that are located at HTTP://www.myserver.com:8083/user say you will also have methods activate and deactivate (say both GET) at URLs HTTP://www.myserver.com:8083/user/activate/ and HTTP://www.myserver.com:8083/user/deactivate. So, in this case, your Http client will set all required timeouts and other configurations and it will also have pre-set target URL HTTP://www.myserver.com:8083/user. and it will have six methods as mentioned above, where each one will simply invoke the parent class method sendHttpRequest(). Of course, for activate and deactivate methods you will need to append "activate" and "deactivate" suffixes for pre-set base URL. So, for each REST service, you can create a dedicated Http client with very minimal effort since the base class already does most of the job. In addition to it, I wrote a self-populating factory for any group of classes that implement the same interface. With that factory, all you will have to do is to write your additional Http client and the factory will detect it and will make it available on its own by predefined name or by the name of the class (based on your choice). All this worked so well for me, that I packaged it into Open Source library called MgntUtils and published it on Maven and Github (with source code and Javadoc. JavaDoc is available here ). A detailed explanation on the Self-populating factory can be seen in Javadoc here . Also, the general article about the library can be found here and specific article about the idea and the implementation of self-populating Factory can be found here . Package com.mgnt.lifecycle.management.example in the source code contains a working example. I hope this helps.

Using parameterized construction of the RestTemplate bean solved my problem. The bean is now configured as:

    @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);
    }

Instead of @Autowiring this field, it can be injected in may be @PostConstruct method like depicted below. The dependent bean can do following:

@Autowire
BeanFactory beanFactory;

RestTemplate restTemplate;

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

Here you can have your bean with custom settings injected in restTemplate .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM