阅读量:0
添加依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.zzc</groupId> <artifactId>Demo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <groupId>com.zzc.component</groupId> <artifactId>component</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- 公共类、日志依赖等 --> <!-- <dependency> <groupId>com.zzc.common</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- springboot中包含了httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> </project>
定义配置类Properties
package com.zzc.component.http; import lombok.Data; import lombok.ToString; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.Map; @Data @ToString @ConfigurationProperties(prefix = "httpclient") public class HttpClientProperties { private String charset = "UTF-8"; /** * 总链接数 */ private Integer connMaxTotal = 3000; /** * 并发数量 */ private Integer maxPerRoute = 1200; /** * 重试次数 */ private Integer retryNum = 1; /** * 链接超时 */ private Integer connectTimeout = 30000; /** * 读写超时 */ private Integer readTimeout = 15000; /** * 链接不够用的等待时间,不宜过长,必须设置 */ private Integer requestTimeout = 200; /** * 默认链接保持时间,单位 秒 */ private Integer keepAliveTime = 60; /** * 如果请求目标地址单独配置可长链接保持时间,使用该配置 */ private Map<String, Integer> keepAliveTargetHost; /** * http请求header */ private Map<String, String> headers; }
初始化httpclient
package com.zzc.component.http; import lombok.extern.slf4j.Slf4j; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpHost; import org.apache.http.client.HttpClient; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @Slf4j @Configuration @ConditionalOnClass(value = {RestTemplate.class, CloseableHttpClient.class}) @Import(HttpClientProperties.class) public class HttpClientFactory { private HttpClientProperties httpClientProperties; public HttpClientFactory(HttpClientProperties httpClientProperties) { this.httpClientProperties = httpClientProperties; } @Bean(name = "clientHttpRequestFactory") public ClientHttpRequestFactory clientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory factory = httpComponentsClientHttpRequestFactory(); return factory; } protected HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); factory.setReadTimeout(httpClientProperties.getReadTimeout()); factory.setConnectionRequestTimeout(httpClientProperties.getRequestTimeout()); factory.setHttpClient(httpClient()); return factory; } public HttpClient httpClient() { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); try { Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager(registry); httpClientConnectionManager.setMaxTotal(httpClientProperties.getConnMaxTotal()); httpClientConnectionManager.setDefaultMaxPerRoute(httpClientProperties.getMaxPerRoute()); httpClientBuilder.setConnectionManager(httpClientConnectionManager); httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(httpClientProperties.getRetryNum(), httpClientProperties.getRetryNum() != 0)); List<Header> headers = genHeaders(); if (headers != null && !headers.isEmpty()) { httpClientBuilder.setDefaultHeaders(headers); } httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy()); //设置定时关闭无效链接 httpClientBuilder.evictIdleConnections(30L, TimeUnit.SECONDS); return httpClientBuilder.build(); } catch (Exception e) { log.error("init http factory error", e); } return null; } private ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return ((httpResponse, httpContext) -> { HeaderElementIterator it = new BasicHeaderElementIterator(httpResponse.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String name = he.getName(); String value = he.getValue(); if (value != null && "timeout".equalsIgnoreCase(name)) { try { return Long.parseLong(value) * 1000L; } catch (NumberFormatException ignore) { log.error("resolve Keep-Alive timeout", ignore); } } } HttpHost target = (HttpHost) httpContext.getAttribute(HttpClientContext.HTTP_TARGET_HOST); //如果请求的目标地址单独做了配置,使用以下的设置 Optional<Map.Entry<String, Integer>> any = Optional.ofNullable(httpClientProperties.getKeepAliveTargetHost()) .orElseGet(HashMap::new) .entrySet() .stream() .filter(e -> e.getKey().equalsIgnoreCase(target.getHostName())) .findAny(); int keepAliveTime = httpClientProperties.getKeepAliveTime() == null ? 60 : httpClientProperties.getKeepAliveTime(); return any.map(e -> e.getValue() * 1000L).orElse(keepAliveTime * 1000L); }); } private List<Header> genHeaders() { List<Header> headers = new ArrayList<>(); if (httpClientProperties.getHeaders() == null) { log.warn("init header is null"); return headers; } for (Map.Entry<String, String> entry : httpClientProperties.getHeaders().entrySet()) { headers.add(new BasicHeader(entry.getKey(), entry.getValue())); } return headers; } private void modifyDefaultCharset(RestTemplate restTemplate) { List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); HttpMessageConverter<?> converterTarget = null; for (HttpMessageConverter<?> item : converterList) { if (StringHttpMessageConverter.class == item.getClass()) { log.info("HttpMessageConvert exist null"); converterTarget = item; break; } } if (null != converterTarget) { converterList.remove(converterTarget); } Charset defaultCharset = Charset.forName(httpClientProperties.getCharset()); converterList.add(1, new StringHttpMessageConverter(defaultCharset)); } private RestTemplate createRestTemplate(ClientHttpRequestFactory clientHttpRequestFactory) { RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory); modifyDefaultCharset(restTemplate); restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); return restTemplate; } @Bean(name = "httpClientRestTemplate") public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) { return createRestTemplate(clientHttpRequestFactory); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
定义接口
package com.zzc.component.http; import org.springframework.web.client.ResponseExtractor; import java.util.Map; public interface RestTemplateComponent { <T> T post(String url, Map<String, String> header, Object body, Class<T> clazz); <T> T post(String url, Object body, Class<T> clazz); <T> T get(String url, Map<String, String> header, Object params, Class<T> clazz); <T> T get(String url, Object params, Class<T> clazz); /** * get请求的文件代理传输等 * @param url * @param headerMap * @param extractor * @return * @param <T> */ <T> T transfer(String url, Map<String, String> headerMap, ResponseExtractor<T> extractor); }
实现接口
package com.zzc.component.http; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @Slf4j @Component public class RestTemplateComponentImpl implements RestTemplateComponent { @Qualifier("httpClientRestTemplate") @Autowired private RestTemplate restTemplate; @Override public <T> T post(String url, Map<String, String> header, Object body, Class<T> clazz) { log.info("rest post url:{}", url); Set<Map.Entry<String, String>> entries = header.entrySet(); MultiValueMap<String, String> headers = new HttpHeaders(); for (Map.Entry<String, String> entry : entries) { List<String> objList = new ArrayList<>(); objList.add(entry.getValue()); headers.put(entry.getKey(), objList); } HttpEntity<Map<String, Object>> formEntity = new HttpEntity(body, headers); ResponseEntity<T> response = restTemplate.postForEntity(url, formEntity, clazz); if (response == null) { log.error("request error, url:{}", url); return null; } return response.getBody(); } @Override public <T> T post(String url, Object body, Class<T> clazz) { HttpEntity<Map<String, Object>> formEntity = new HttpEntity(body, defaultHeaders()); ResponseEntity<T> response = restTemplate.postForEntity(url, formEntity, clazz); if (response == null) { log.error("request error, url:{}", url); return null; } return response.getBody(); } @Override public <T> T get(String url, Map<String, String> headers, Object params, Class<T> clazz) { ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null, generateHeader(headers)), clazz, params); if (response == null) { log.error("request error, url:{}", url); return null; } return response.getBody(); } @Override public <T> T get(String url, Object params, Class<T> clazz) { ResponseEntity<T> response = restTemplate.getForEntity(url, clazz, params); if (response == null) { log.error("request error, url:{}", url); return null; } return response.getBody(); } @Override public <T> T transfer(String url, Map<String, String> headerMap, ResponseExtractor<T> extractor) { RequestCallback requestCallback = null; if (headerMap == null || headerMap.isEmpty()) { requestCallback = restTemplate.httpEntityCallback(HttpEntity.EMPTY); } else { HttpEntity<Map<String, Object>> formEntity = new HttpEntity<>(null, generateHeader(headerMap)); requestCallback = restTemplate.httpEntityCallback(formEntity); } return restTemplate.execute(url, HttpMethod.GET, requestCallback, extractor); } /** * 如果请求的header,Content-Type=application/x-www-form-urlencoded * 则需参数接收的载体需要使用 MultiValueMap * @return */ private static MultiValueMap<String, String> defaultHeaders() { Map<String, String> header = new HashMap<>(); header.put("Content-Type", "application/json;charset=utf-8"); Set<Map.Entry<String, String>> entries = header.entrySet(); MultiValueMap<String, String> headers = new HttpHeaders(); for (Map.Entry<String, String> entry : entries) { List<String> objList = new ArrayList<>(); objList.add(entry.getValue()); headers.put(entry.getKey(), objList); } return headers; } private static MultiValueMap<String, String> generateHeader(Map<String, String> headerMap) { if (headerMap == null || headerMap.isEmpty()) { return defaultHeaders(); } MultiValueMap<String, String> headers = new HttpHeaders(); for (Map.Entry<String, String> entry : headerMap.entrySet()) { List<String> objList = new ArrayList<>(); objList.add(entry.getValue()); headers.put(entry.getKey(), objList); } return headers; } }