• 适配不同场景的RestTemplate


    一个基本实现

    如果项目里可能只是偶尔通过一个url,发起一个http请求,一个基本实现如下:

    1. @Configuration
    2. public class RestTemplateConfiguration {
    3. @Bean
    4. public RestTemplate restTemplate() {
    5. RestTemplate restTemplate = new RestTemplate();
    6. // 这个转换器非必须,根据自己的场景添加
    7. restTemplate.getMessageConverters().set(1,
    8. new StringHttpMessageConverter(Charset.forName("UTF-8")));
    9. return restTemplate;
    10. }
    11. }

    进行一下测试:

    很好,没有问题。

    配置超时

    默认实现,比如读取超时或者连接超时是没有限制,我们想自定义,实现如下:

    1. @Bean
    2. public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    3. RestTemplate restTemplate = new RestTemplate(factory);
    4. // 这个转换器非必须,根据自己的场景添加
    5. restTemplate.getMessageConverters().set(1,
    6. new StringHttpMessageConverter(Charset.forName("UTF-8")));
    7. return restTemplate;
    8. }
    9. @Bean
    10. public ClientHttpRequestFactory clientHttpRequestFactory() {
    11. SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
    12. simpleClientHttpRequestFactory.setReadTimeout(3000);
    13. simpleClientHttpRequestFactory.setReadTimeout(3000);
    14. return simpleClientHttpRequestFactory;
    15. }

    我们可以定义一个ClientHttpRequestFactory并配置超时时间来初始化RestTemplate

    RestTemplate默认也是用的ClientHttpRequestFactory的。

    支持连接池

    默认的ClientHttpRequestFactory使用的是HttpUrlConnection,本身是不支持连接池的,这个时候我们需要启用连接池的实现来提高一个吞吐量或者减少请求响应时间,怎么办。

    替换默认的ClientHttpRequestFactory,如下,我们使用支持 Apache HttpComponents HttpClient的实现。

    1. @Bean(name = "clientHttpRequestFactory")
    2. public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient client) {
    3. HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new
    4. HttpComponentsClientHttpRequestFactory(client);
    5. clientHttpRequestFactory.setConnectTimeout(httpClientProperties.getConnectTimeout());
    6. clientHttpRequestFactory.setReadTimeout(httpClientProperties.getReadTimeout());
    7. clientHttpRequestFactory.setConnectionRequestTimeout(httpClientProperties.getAcquireConnectionTimeout());
    8. return clientHttpRequestFactory;
    9. }
    10. @Bean
    11. public HttpClient httpClient() {
    12. HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    13. try {
    14. // 针对https协议相关配置
    15. SSLContext sslContext = SSLContext.getInstance("SSL");// 获取一个SSLContext实例
    16. TrustManager[] trustAllCerts = {new InsecureTrustManager()};
    17. sslContext.init(null, trustAllCerts, new java.security.SecureRandom());// 初始化SSLContext实例
    18. //设置信任ssl访问
    19. httpClientBuilder.setSSLContext(sslContext);
    20. HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
    21. SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
    22. Registry socketFactoryRegistry = RegistryBuilder.create()
    23. // 注册http和https请求
    24. .register("http", PlainConnectionSocketFactory.getSocketFactory())
    25. .register("https", sslConnectionSocketFactory).build();
    26. PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    27. poolingHttpClientConnectionManager.setMaxTotal(httpClientProperties.getMaxConnection());
    28. poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpClientProperties.getMaxConnectionRoute());
    29. httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
    30. httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(httpClientProperties.getRetryTimes(), true));
    31. //设置默认请求头
    32. List
      headers = getDefaultHeaders();
    33. httpClientBuilder.setDefaultHeaders(headers);
    34. httpClientBuilder.evictExpiredConnections();
    35. httpClientBuilder.evictIdleConnections(httpClientProperties.getIdleTime(), TimeUnit.MINUTES);
    36. CloseableHttpClient httpClient = httpClientBuilder.build();
    37. Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    38. try {
    39. httpClient.close();
    40. } catch (IOException e) {
    41. log.error("close http client error.", e);
    42. }
    43. }));
    44. return httpClient;
    45. } catch (Exception e) {
    46. log.error("HttpClient create error.", e);
    47. }
    48. return null;
    49. }
    50. private List
      getDefaultHeaders() {
    51. List
      headers = new ArrayList<>();
    52. headers.add(new BasicHeader("Connection", "Keep-Alive"));
    53. return headers;
    54. }
    55. class InsecureTrustManager implements X509TrustManager {
    56. @Override
    57. public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
    58. @Override
    59. public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
    60. /**
    61. * 返回受信任的X509证书数组
    62. */
    63. @Override
    64. public X509Certificate[] getAcceptedIssuers() {
    65. return null;
    66. }
    67. }

     好吧,这里的代码就有点多了,其中使用的HttpClientProperties类主要是其中的一些属性配置,如下:

    1. public class HttpClientProperties {
    2. private int readTimeout;
    3. private int connectTimeout;
    4. private int acquireConnectionTimeout;
    5. private int maxConnection;
    6. private int maxConnectionRoute;
    7. private int retryTimes;
    8. private int idleTime;
    9. }

    这样,再创建RestTemplate的时候,我们可以指定使用支持连接池的ClientHttpRequestFactory,如下:

    1. @Resource(name = "clientHttpRequestFactory")
    2. @Bean
    3. public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    4. RestTemplate restTemplate = new RestTemplate(factory);
    5. restTemplate.getMessageConverters().set(1,
    6. new StringHttpMessageConverter(Charset.forName("UTF-8")));
    7. return restTemplate;
    8. }

    支持服务发现

    我们的项目里集成了注册中心,需要支持服务发现,负载均衡调用,加个注解即可,如下:

    1. @LoadBalanced
    2. @Bean
    3. public RestTemplate restTemplate() {
    4. RestTemplate restTemplate = new RestTemplate();
    5. return restTemplate;
    6. }

    注意,这种的就不支持普通的url调用了。

    服务发现与普通URL

    如果项目里既需要支持服务发现进行调用,又需要调用一些常规的http url的接口,也很简单,定义两个,需要哪个注入哪个:

    1. @LoadBalanced
    2. @Bean(name = "loadBalancedRestTemplate")
    3. public RestTemplate loadBalancedRestTemplate() {
    4. RestTemplate restTemplate = new RestTemplate();
    5. return restTemplate;
    6. }
    7. @Bean(name = "restTemplate")
    8. public RestTemplate restTemplate() {
    9. RestTemplate restTemplate = new RestTemplate();
    10. return restTemplate;
    11. }

    响应状态错误码处理

    默认实现,如果是4xx或5xx错误,请求响应的时候会抛出异常:

     在我们的一些场景中,某些接口可能会通过返回不同的状态码来返回不同的错误信息,而不是都返回一个200的状态码,在消息体使用code等字段来表示,如果是4xx或5xx的时候不希望抛异常,而由我们自己获取判断处理,可以如下定义:

    1. @Bean
    2. public RestTemplate restTemplate() {
    3. RestTemplate restTemplate = new RestTemplate();
    4. restTemplate.getMessageConverters().set(1,
    5. new StringHttpMessageConverter(Charset.forName("UTF-8")));
    6. restTemplate.setErrorHandler(new ResponseErrorHandler() {
    7. @Override
    8. public boolean hasError(ClientHttpResponse response) throws IOException {
    9. return false;
    10. }
    11. @Override
    12. public void handleError(ClientHttpResponse response) throws IOException {
    13. }
    14. });
    15. return restTemplate;
    16. }
  • 相关阅读:
    〖大前端 - ES6篇②〗- let和const
    C#:实现堆排序算法(附完整源码)
    WSL安装和嵌入式Linux的树莓派环境设置和交叉编译
    图论第1天----第797题、第200题、第695题
    Arduino平台软硬件原理及使用——开源库的使用
    Rust bin 文件比较差异
    深度解析linux内核模块编译makefile
    几分钟查询快递单号物流,一分钟筛选出未签收单号
    (附源码)计算机毕业设计SSM基于框架的在线问答平台
    手把手教你解决spring boot导入swagger2版本冲突问题,刘老师教编程
  • 原文地址:https://blog.csdn.net/x763795151/article/details/127883040