HttpClient连接池小记

我不是女神ヾ 2021-11-09 20:20 502阅读 0赞

**欢迎关注公众号**

20191121223121694.jpg

**微信扫一扫**

【仅需9.9订阅专栏合集,作者所有付费文章都能看(持续更新)】

推荐【Kafka教程】https://bigbird.blog.csdn.net/article/details/108770504
推荐【rabbitmq教程】https://bigbird.blog.csdn.net/article/details/81436980
推荐【Flink教程】https://blog.csdn.net/hellozpc/article/details/109413465
推荐【JVM面试与调优教程】https://bigbird.blog.csdn.net/article/details/113888604
推荐【SpringBoot全套教程】https://blog.csdn.net/hellozpc/article/details/107095951
推荐【SpringCloud教程】https://blog.csdn.net/hellozpc/article/details/83692496
推荐【Mybatis教程】https://blog.csdn.net/hellozpc/article/details/80878563
推荐【SnowFlake教程】https://blog.csdn.net/hellozpc/article/details/108248227
推荐【并发限流教程】https://blog.csdn.net/hellozpc/article/details/107582771
推荐【Redis教程】https://bigbird.blog.csdn.net/article/details/81267030
推荐【Netty教程】https://blog.csdn.net/hellozpc/category_10945233.html

推荐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)

        1. .evictIdleConnections(1, TimeUnit.MINUTES)// 定期回收空闲连接
        2. .evictExpiredConnections()// 定期回收过期连接
        3. .setConnectionTimeToLive(1, TimeUnit.MINUTES) // 连接存活时间,如果不设置,则根据长连接信息决定
        4. .setDefaultRequestConfig(
        5. RequestConfig
        6. .custom()
        7. .setAuthenticationEnabled(false)
        8. .setCircularRedirectsAllowed(false)
        9. .setSocketTimeout(DEFAULT_READ_TIMEOUT)
        10. .setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT)
        11. .setConnectionRequestTimeout(1000)
        12. .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
        13. .build()) // 设置默认请求配置
        14. .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) // 连接重用策略 是否能keepAlive
        15. .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)// 长连接配置,即获取长连接生产多长时间
        16. .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))// 重试次数 默认3次 此处禁用
        17. .build();

        Registry socketFactoryRegistry =

        1. RegistryBuilder.<ConnectionSocketFactory>create()
        2. .register("https", PlainConnectionSocketFactory.getSocketFactory())
        3. .register("http", new PlainConnectionSocketFactory()).build();

        final int conExpire = 15;// 长连接闲置过期时间,可配置
        cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, conExpire,

        1. 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(() -> {

        1. while (true) {
        2. try {
        3. Thread.sleep(10000);
        4. cm.closeExpiredConnections();
        5. cm.closeIdleConnections(conExpire, TimeUnit.SECONDS);
        6. } catch (Exception e) {
        7. logger.error("stale check exception", e);
        8. }
        9. }

        }, “HttpInvoker-coonection-stale-check-thread”);
        // 设置成为守护线程
        staleCheckThread.setDaemon(true);
        staleCheckThread.start();

        // 程序退出时,关闭httpClient
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {

        1. try {
        2. httpClient.close();
        3. logger.info(">>>>httpClient closed<<<<");
        4. } catch (Exception e) {
        5. logger.warn("httpClient close exception", e);
        6. }

        }, “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) {

        1. typeReference = (TypeReference<R>) new TypeReference<Response>() {
        2. };

        }

        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)

        1. .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 {

    1. public String id;
    2. public String orderCode;
    3. public String startCityName;
    4. public String startAddress;
    5. public String startAddDtl;
    6. public String startLongitude;
    7. public String startLatitude;
    8. public String endCityName;
    9. public String endAddress;
    10. public String endAddDtl;
    11. public String endLongitude;
    12. public String endLatitude;
    13. public byte prodType;
    14. public LocalDateTime orderDate;
    15. public int orderDay;
    16. public String flightNo;
    17. public String remark;
    18. public int orderState;
    19. public String driverId;
    20. public String driverUid;
    21. public String driverMobile;
    22. public String source;
    23. public LocalDateTime createDate;
    24. public LocalDateTime updateDate;
    25. public String dispatchType;
    26. public String driverRemark;
    27. public LocalDateTime flightDate;
    28. public String flightArrCode;
    29. public String flightDepCode;
    30. public String cancleRemarks;
    31. 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) {
      1. return res != null && res.retCode == SUCCESS_CODE;
      }
      @Override
      public String toString() {
      1. return "Response{" +
      2. "retCode=" + retCode +
      3. ", retMsg='" + retMsg + '\'' +
      4. '}';
      }
      }

    public class OrderResponse extends Response {

    1. public List<Order> info;

    }

**欢迎关注公众号**

20191121223121694.jpg

**微信扫一扫**

发表评论

表情:
评论列表 (有 0 条评论,502人围观)

还没有评论,来说两句吧...

相关阅读

    相关 HTTPClient 连接

      向Dirud集群写入数据时,采用HTTPClient向集群提交数据,由于数据量大,需要以多线程方式开启多个线程,单个的HTTPClient会出现连接超时,效率低的情况。高