优秀的编程知识分享平台

网站首页 > 技术文章 正文

http请求工具之RestTemplate

nanyue 2024-11-24 19:44:17 技术文章 1 ℃

springboot中使用spring-boot-starter-web自带RestTemplate

自定义HttpComponentsClientHttpRequestFactory 需要引用maven坐标

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.12</version>
</dependency>

代码配置:

//读取properties配置

@Configuration
@ConfigurationProperties(prefix = "resttemplate-http-pool")
      public class RestTemplateConfigProperties {
      private int connectionRequestTimeout = 1000;
      private int connectionTimeout = 2 * 1000;
      private int socketTimeout = 3 * 1000;
      private int maxPerRoute = 10;
      private int maxTotal = 50;
      private int validateAfterInactivity = 3 * 1000;
      private int maxIdleTime = 30 * 1000;
      private int retryTimes = 0;
      //省略 get、set
}
@Configuration
public class RestTemplateConfig {
    private Logger logger = LoggerFactory.getLogger(RestTemplateConfig.class);

    @Autowired
    private RestTemplateConfigProperties restTemplateConfigProperties;

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient()));
        restTemplate.setErrorHandler(new CustomErrorHandler());
        return restTemplate;
    }

      @Bean
      public HttpClient httpClient() {
            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
            connectionManager.setMaxTotal(restTemplateConfigProperties.getMaxTotal());
            connectionManager.setDefaultMaxPerRoute(restTemplateConfigProperties.getMaxPerRoute());
            //在从连接池获取连接时,连接不活跃多长时间后需要进行一次验证,默认为2s
            connectionManager.setValidateAfterInactivity(restTemplateConfigProperties.getValidateAfterInactivity());
            RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(restTemplateConfigProperties.getSocketTimeout())
            .setConnectTimeout(restTemplateConfigProperties.getConnectionTimeout())
            .setConnectionRequestTimeout(restTemplateConfigProperties.getConnectionRequestTimeout())
            .build();
            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
            .setDefaultRequestConfig(requestConfig)
            .setConnectionManager(connectionManager)
            //定期回收空闲连接
            .evictIdleConnections(restTemplateConfigProperties.getMaxIdleTime(), TimeUnit.MILLISECONDS)
            //回收过期连接
            .evictExpiredConnections();
            if (restTemplateConfigProperties.getRetryTimes() > 0) {
            			httpClientBuilder.setRetryHandler(handler);
            } else {
           			 httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
            }
            			CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            /**
            *JVM停止或重启时,关闭连接池释放掉连接
            */
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                  logger.info("closing http client");
                  closeableHttpClient.close();
                  logger.info("http client closed");
            } catch (IOException e) {
            			logger.error(e.getMessage(), e);
            }
            }));
            return closeableHttpClient;
      }
static class CustomErrorHandler implements ResponseErrorHandler {
      //自定义异常处理方式这个配置是当RestTemplate 请求异常时将错误返回给使用者自行处理,
     //而不是抛出异常导致使用者无法获取调用情况。
      @Override
      public boolean hasError(ClientHttpResponse response) throws IOException {
      		return true;
      }
      @Override
      public void handleError(ClientHttpResponse response) throws IOException {
      }
}
      //重试策略,注意幂等操作,不需要可以使用默认的
      HttpRequestRetryHandler handler = (arg0, retryTimes, arg2) -> {
            if (retryTimes > restTemplateConfigProperties.getRetryTimes()) {
           			 return false;
            }
                //Timeout
            if (arg0 instanceof InterruptedIOException) {
                return false;
            }
            //Unknown host
            if (arg0 instanceof UnknownHostException) {
          		  return false;
            }
            //SSL handshake exception
            if (arg0 instanceof SSLException) {
            			return false;
      			}
              HttpClientContext clientContext = HttpClientContext.adapt(arg2);
              HttpRequest request = clientContext.getRequest();
              //如果请求类型不是HttpEntityEnclosingRequest,被认为是幂等的,那么就重试
              //HttpEntityEnclosingRequest指的是有请求体的request,比HttpRequest多一个Entity属性
              //而常用的GET请求是没有请求体的,POST、PUT都是有请求体的
              //Rest一般用GET请求获取数据,故幂等,POST用于新增数据,故不幂等
              boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
              return idempotent;
        };
}

重点参数解释

maxPerRoute

服务1要通过httpclient调用服务2的接口。服务1发送了400个请求,当设置maxPerRoute=100,MaxTotal=200,接口执行时间为500ms,由于maxPerRoute=100,所以要分为100,100,100,100分四批来执行,全部执行完成需要2000ms。而如果maxPerRoute设置为400,全部执行完需要500ms。在这种情况下(提供并发能力时)就要对这两个参数进行设置了。

MaxTotal

限制多个maxPerRoute总数不能大于该参数;

connectTimeOut

指建立连接的超时时间,比较容易理解

connectionRequestTimeOut

指从连接池获取到连接的超时时间,如果是非连接池的话,该参数暂时没有发现有什么用处

socketTimeOut

指客户端和服务进行数据交互的时间,是指两者之间如果两个数据包之间的时间大于该时间则认为超时,而不是整个交互的整体时间,比如如果设置1秒超时,如果每隔0.8秒传输一次数据,传输10次,总共8秒,这样是不超时的。而如果任意两个数据包之间的时间超过了1秒,则超时。

properties配置属性:

resttemplate-http-pool.connectionRequestTimeout=60000
resttemplate-http-pool.connectionTimeout=60000
resttemplate-http-pool.socketTimeout=60000
resttemplate-http-pool.maxPerRoute=10
resttemplate-http-pool.maxTotal=50
resttemplate-http-pool.validateAfterInactivity=3000
resttemplate-http-pool.maxIdleTime=30000
resttemplate-http-pool.retryTimes=3

封装工具类:

//post表单类型

public String postForm(String targetUrl, MultiValueMap<String, Object> paraMap) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(paraMap, headers);
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(targetUrl, request, String.class);
    return responseEntity.getBody();
}

//post json类型

public String postJson(String targetUrl, String jsonPara) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity(jsonPara, headers);
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(targetUrl, request, String.class);
    return responseEntity.getBody();
}

//get http://127.0.0.1:9513/httpTestC/formUriVariablesTestReceive?key1=val1&key2=val2类型

public String getWithUriAppend(String targetUrl, Map<String, Object> paraMap) {
      if (paraMap == null || paraMap.isEmpty()) {
      ResponseEntity<String> responseEntity = restTemplate.getForEntity(targetUrl, String.class);
      return responseEntity.getBody();
      }
      StringBuffer url = new StringBuffer(targetUrl.concat("?"));
      for (Map.Entry<String, Object> entry : paraMap.entrySet()) {
      url.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
      }
      targetUrl = StringUtils.substringBeforeLast(url.toString(), "&");
      ResponseEntity<String> responseEntity = restTemplate.getForEntity(targetUrl, String.class, paraMap);
      return responseEntity.getBody();
}

//get http://127.0.0.1:9513/httpTestC/formGetTestReceive/{key1}/{key2} 类型

public String getWithUriVariables(String targetUrl, Map<String, Object> paraMap) {
    if (paraMap == null || paraMap.isEmpty()) {
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(targetUrl, String.class);
        return responseEntity.getBody();
    }
    StringBuffer url = new StringBuffer(targetUrl);
    for (Map.Entry<String, Object> entry : paraMap.entrySet()) {
      	url.append("/").append("{").append(entry.getKey()).append("}");
    }
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(url.toString(), String.class, paraMap);
    return responseEntity.getBody();
}
最近发表
标签列表