HttpClient连接池

推荐Springboot集成http连接池 https://blog.csdn.net/hellozpc/article/details/106861972

  • 常用的http客户端有JDK原生的URLConnection、Netty的异步HTTP Client、Spring的RestTemplate、Spring Cloud中的Feign。
    虽然RestTemplate、Feign使用极其方便,但是屏蔽了太多底层细节,不利于全局把控。

  • 本文主要记载一下基于Apache HttpClient 的http连接池处理。网上很多文章都没有关注定期检测关闭无效连接这块功能。另外要注册jvm钩子在程序退出时关闭httpClient实例。

  • /**
     * 业务层调用HttpTemplate#execute发送http post请求
     * @author zhoupengcheng
     */
    public class HttpTemplate {
    
        private static Logger logger = LoggerFactory.getLogger(HttpTemplate.class);
    
        private static final String DEFAULT_ACCEPTTYPE = "application/json;charset=utf-8";
        private static final String DEFAULT_CHARSET = "utf-8";
    
        /**
         * 最大连接数
         */
        private static final int MAX_CONNECTION_NUM = 20;
    
        /**
         * 单路由最大连接数
         */
        private static final int MAX_PER_ROUTE = 20;
    
        /**
         * 连接超时时间,缺省为3秒钟
         */
        private static final int DEFAULT_CONNECTION_TIMEOUT = 3000;
    
        /**
         * 读取超时时间,缺省为3秒钟
         */
        private static final int DEFAULT_READ_TIMEOUT = 3000;
    
        /**
         * 连接池管理对象      
         */
        private PoolingHttpClientConnectionManager cm;
    
        private CloseableHttpClient httpClient;
    
        /**
         * 反序列化工具
         */
        private ObjectMapper objectMapper;
    
        public HttpTemplate() {
            init();
        }
    
        public void init() {
            initObjectMapper();
            initHttpClient();
        }
    
        private void initHttpClient() {
            HttpClientBuilder builder = HttpClients.custom();
            builder.disableCookieManagement();
            builder.disableRedirectHandling();
            builder.disableAutomaticRetries();
    
            builder.setConnectionManagerShared(false)
                    .evictIdleConnections(1, TimeUnit.MINUTES)// 定期回收空闲连接
                    .evictExpiredConnections()// 定期回收过期连接
                    .setConnectionTimeToLive(1, TimeUnit.MINUTES) // 连接存活时间,如果不设置,则根据长连接信息决定
                    .setDefaultRequestConfig(
                            RequestConfig
                                    .custom()
                                    .setAuthenticationEnabled(false)
                                    .setCircularRedirectsAllowed(false)
                                    .setSocketTimeout(DEFAULT_READ_TIMEOUT)
                                    .setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT)
                                    .setConnectionRequestTimeout(1000)
                                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                                    .build()) // 设置默认请求配置
                    .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) // 连接重用策略 是否能keepAlive
                    .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)// 长连接配置,即获取长连接生产多长时间
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))// 重试次数 默认3次 此处禁用
                    .build();
    
            Registry socketFactoryRegistry =
                    RegistryBuilder.create()
                            .register("https", PlainConnectionSocketFactory.getSocketFactory())
                            .register("http", new PlainConnectionSocketFactory()).build();
    
            final int conExpire = 15;// 长连接闲置过期时间,可配置
            cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, conExpire,
                    TimeUnit.SECONDS);
            cm.setDefaultConnectionConfig(ConnectionConfig.DEFAULT);
            cm.setMaxTotal(MAX_CONNECTION_NUM);
            cm.setDefaultMaxPerRoute(MAX_PER_ROUTE);
            // 设置长连接心跳检测,设置超时,禁用nagle算法
            cm.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(DEFAULT_READ_TIMEOUT).setTcpNoDelay(true).build());
            cm.setValidateAfterInactivity(-1);// 每次取重用的连接时禁止连接活性检测,可以提升性能
    
            builder.setConnectionManager(cm);
            builder.setConnectionManagerShared(false);
    
            httpClient = builder.build();
    
            // 过期检测
            Thread staleCheckThread = new Thread(() -> {
                while (true) {
                    try {
                        Thread.sleep(10000);
                        cm.closeExpiredConnections();
                        cm.closeIdleConnections(conExpire, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        logger.error("stale check exception", e);
                    }
                }
    
            }, "HttpInvoker-coonection-stale-check-thread");
            // 设置成为守护线程
            staleCheckThread.setDaemon(true);
            staleCheckThread.start();
    
            // 程序退出时,关闭httpClient
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    httpClient.close();
                    logger.info(">>>>httpClient closed<<<<");
                } catch (Exception e) {
                    logger.warn("httpClient close exception", e);
                }
            }, "srsynchronize-httpInvoker-shutdown-thread"));
        }
    
        private void initObjectMapper() {
            objectMapper = new ObjectMapper();
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
            objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        }
    
        /**
         * 暴露给业务层调用的方法
         * @param path          请求url
         * @param req           请求对象
         * @param typeReference 响应对象
         * @param 
         * @return
         * @throws Exception
         */
        public  R execute(String path, Object req, TypeReference typeReference) throws Exception {
            if (typeReference == null) {
                typeReference = (TypeReference) new TypeReference() {
                };
            }
    
            String reqBody = objectMapper.writeValueAsString(req);
            logger.info("reqBody:{}", reqBody);
    
            HttpPost httpPost = new HttpPost(path);
            httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
            httpPost.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE);
            httpPost.setConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_READ_TIMEOUT)
                    .setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT).build());
    
            httpPost.addHeader("Accept", DEFAULT_ACCEPTTYPE);
            httpPost.addHeader("Content-Type", DEFAULT_ACCEPTTYPE);
    
            HttpEntity httpEntity = new StringEntity(reqBody, DEFAULT_CHARSET);
            httpPost.setEntity(httpEntity);
    
            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            String responseBody = EntityUtils.toString(httpResponse.getEntity(), DEFAULT_CHARSET);
            logger.info("resBody:{}", responseBody);
            return objectMapper.readValue(responseBody, typeReference);
        }
    
        /**
         * demo
         *
         * @param args
         * @throws Exception
         */
        public static void main(String[] args) throws Exception {
            HttpTemplate httpTemplate = new HttpTemplate();
            Order order = new   Order();
            order.id = "1111111";
            order.cancleRemarks = "擦擦擦";
            order.createDate = LocalDateTime.now();
            order.startLongitude = "1356.4343";
            OrderResponse orderResponse = httpTemplate.execute("http://127.0.0.1:8888/ordersdispatch", order, new TypeReference() {
            });
    
            //不传具体的OrderResponse会使用默认的Response
            Response response = httpTemplate.execute("http://127.0.0.1:8888/orderdispatch", order, null);
    
            logger.info("orderResponse:{}", orderResponse);
            logger.info("response:{}", response);
        }
    
    }
    
    
    public class Order {
    
    	public String id;
    	public String orderCode;
    	public String startCityName;
    	public String startAddress;
    	public String startAddDtl;
    	public String startLongitude;
    	public String startLatitude;
    	public String endCityName;
    	public String endAddress;
    	public String endAddDtl;
    	public String endLongitude;
    	public String endLatitude;
    	public byte prodType;
    	public LocalDateTime orderDate;
    	public int orderDay;
    	public String flightNo;
    	public String remark;
    	public int orderState;
    	public String driverId;
    	public String driverUid;
    	public String driverMobile;
    	public String source;
    	public LocalDateTime createDate;
    	public LocalDateTime updateDate;
    	public String dispatchType;
    	public String driverRemark;
    	public LocalDateTime flightDate;
    	public String flightArrCode;
    	public String flightDepCode;
    	public String cancleRemarks;
    	public LocalDateTime endTime;
    
    }
    
    /**
     * http响应实体
     */
    public class Response {
        public static int SUCCESS_CODE = 0;
        public Integer retCode;
        public String retMsg;
        public static boolean isSuccess(Response res) {
            return res != null && res.retCode == SUCCESS_CODE;
        }
        @Override
        public String toString() {
            return "Response{" +
                    "retCode=" + retCode +
                    ", retMsg='" + retMsg + '\'' +
                    '}';
        }
    }
    
    public class OrderResponse extends Response {
    
        public List info;
    
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值