如何为 Spring 中的每个客户端配置和构建自定义 RestTemplate?

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

我正在使用 Spring RestTemplate 从我的应用程序执行 HTTP 请求。我们需要调用几个服务,有的在互联网上,有的在内网,有的快,有的慢。我被指示为每个服务配置自定义设置,基本上是连接超时、读取超时。

这些设置将非常具体,例如,托管在 Intranet 上的服务的超时时间约为 2-5 秒,而它们为 99.9% 的请求提供 1000 毫秒的 SLA。而其他第三方服务大约需要 10-60 秒。

由于这些参数只能在模板的工厂中设置,因此我创建了许多具有不同工厂的 bean,仅超时时间不同。像这样:

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

恐怕这最终会造成维护方面的噩梦。可以用更好的方法解决吗?

PS: 该应用程序是一个调用各种服务的整体。

您将必须创建多个 RestTemplates 并分配超时、连接池大小。连接池将显着提高性能

我已经硬编码了连接属性,您可以从 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;
    }
}

您可以创建多个 RestTemplates 并使用限定符名称自动装配它。

免责声明:我的回答建议使用其他 Http 客户端而不是 Rest 模板 - 如果您必须使用 Rest 模板,我的回答将无关紧要。

我处理过类似的设计问题,这就是我所做的。我写了我自己的 HttpClient class。它比大多数知名的 Http 客户端使用起来简单得多。这个 class 可以单独使用或者(这与你的情况相关)它可以用作一组 classes 的父 class (实现相同的接口)其中每个 class 将是特定 Rest 服务的 Http 客户端。在此 class 上,您可以预先设置目标 URL 和所有参数(例如读取和连接超时等)。预设此 class 后,您需要做的就是调用 sendHttpRequestMethod()。稍微扩展一下 - 假设您有一个带有 CRUD API 的用户休息服务,由特定的 URL 调用使用不同的 HTTP 方法实现,并且可能是不同的 URLs。 (说除了创建(POST)更新(PUT)读取(GET)和删除(DELETE)位于 HTTP://www.myserver.com:8083/user 的方法之外,你还会有方法在 URLs HTTP://www.myserver.com:8083/user/activate/ 和 HTTP://www.myserver.com:8083/[=18= 激活和停用(比如 GET) ]. 因此,在这种情况下,您的 Http 客户端将设置所有必需的超时和其他配置,并且还将具有预设目标 URL HTTP://www.myserver.com:8083/user。并且它将具有上面提到的六个方法,其中每个方法都将简单地调用父 class 方法 sendHttpRequest()。当然,对于 activate 和 deactivate 方法,您需要为预设基础 URL 附加“activate”和“deactivate”后缀。因此,对于每个 REST 服务,您可以轻松创建一个专用的 Http 客户端,因为基础 class 已经完成了大部分工作。除此之外,我为实现相同接口的任何一组 classes 编写了一个自填充工厂。使用该工厂,您所要做的就是编写额外的 Http 客户端,工厂将检测到它并通过预定义的名称或 class 的名称(根据您的选择).所有这些对我来说都非常有效,我将其打包到名为 MgntUtils 的开源库中,并在 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 上发布。源代码中的包 com.mgnt.lifecycle.management.example 包含一个工作示例。希望对您有所帮助。

使用 RestTemplate bean 的参数化构造解决了我的问题。 该 bean 现在配置为:

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

代替@Autowiring 这个字段,它可以被注入可能是@PostConstruct 方法,如下所示。依赖 bean 可以执行以下操作:

@Autowire
BeanFactory beanFactory;

RestTemplate restTemplate;

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

在这里,您可以将具有自定义设置的 bean 注入 restTemplate