apache httpclient速成


导入依赖

     <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
            <version>5.2.1</version>
        </dependency>

快速使用

不关注其他,只需要发起请求,然后收到即可:

        CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建client
        
        HttpPost request = new HttpPost("https://www.baidu.com/"); // 请求信息
        request.setHeader("User-Agent",
                "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
        RequestConfig config =
                RequestConfig.custom().setResponseTimeout(5000, TimeUnit.MILLISECONDS)
                        .build();
        request.setConfig(config); // 设置连接超时等参数
        
        // 发起调用
        String response = httpClient.execute(request, new HttpClientResponseHandler<String>() {
            @Override
            public String handleResponse(ClassicHttpResponse classicHttpResponse) throws HttpException, IOException {
                System.out.println(classicHttpResponse.getCode());
                HttpEntity entity = classicHttpResponse.getEntity();
                InputStream content = entity.getContent();
                return EntityUtils.toString(entity);

            }
        });

        System.out.println(response);

连接池

http的连接池有什么用?
主要好处在于连接复用,减少创建/销毁 tcp 连接的开销(因为三次握手和四次挥手)。

需要注意的是:
1.http连接池不是万能的,过多的长连接会占用服务器资源,导致其他服务受阻
2.http连接池只适用于请求是经常访问同一主机(或同一个接口)的情况下
3.并发数不高的情况下资源利用率低下
4.httpclient是一个线程安全的类,没有必要由每个线程在每次使用时创建,全局保留一个即可。
比如:

    PoolingHttpClientConnectionManager poopManager = new PoolingHttpClientConnectionManager();
        poopManager.setMaxTotal(5); // 设置连接池最大连接数
        poopManager.setDefaultConnectionConfig( // 为所有路由设置连接参数
                ConnectionConfig.custom().setConnectTimeout(1000, TimeUnit.MILLISECONDS)
                .setSocketTimeout(1000, TimeUnit.MILLISECONDS).build());
        poopManager.setMaxPerRoute( // 单独为一个路由设置最大连接数
                new HttpRoute(new HttpHost(InetAddress.getByName("www.baidu.com"), 443)),
                1);

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(poopManager).build();

参数

用如下代码作为示例。设置了连接池最大连接数是20,每个路由最大连接是2。
代码一共有四个路由,每一个网站都用一个异步线程调用10次

package com.example.springbootproject.httpclient;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.pool.PoolStats;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class HttpUtil {

    public static final CloseableHttpClient httpClient;
    public static final PoolingHttpClientConnectionManager poolManager;

    static {
        poolManager = new PoolingHttpClientConnectionManager();
        poolManager.setDefaultMaxPerRoute(2);
        poolManager.setMaxTotal(20);
        httpClient = HttpClients.custom()
                // 设置连接池管理
                .setConnectionManager(poolManager)
                .build();
    }


    public static void main(String[] args) {
        HttpUtil.execute("http://www.baidu.com");
        HttpUtil.execute("https://juejin.cn/");
        HttpUtil.execute("https://www.zhihu.com/hot");
        HttpUtil.execute("https://www.bilibili.com/?utm_source=gold_browser_extension");
        // 创建一个定时线程池,包含单个线程
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        // 安排任务在初始延迟后执行,然后每隔一定时间打印状态
        executor.scheduleAtFixedRate(() -> {
            HttpUtil.httpPoolStats();
        }, 0, 1, TimeUnit.SECONDS);
    }

    public static void execute(String url) {
        for (int i = 0; i < 10; i++) {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            executor.submit(() -> {
                HttpGet get = new HttpGet(url);
                CloseableHttpResponse response = null;
                try {
                    Thread.sleep(1000);
                    String execute = HttpUtil.httpClient.execute(get, new HttpClientResponseHandler<String>() {
                        @Override
                        public String handleResponse(ClassicHttpResponse response) throws HttpException, IOException {
                            return EntityUtils.toString(response.getEntity());
                        }
                    });
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    public static void httpPoolStats() {
        // 获取所有路由的连接池状态
        PoolStats totalStats = poolManager.getTotalStats();
        System.out.println("Total status:" + totalStats.toString());
    }
}

连接池状态

PoolStats totalStats = poolManager.getTotalStats();打印出来是这样的[leased: 0; pending: 0; available: 0; max: 20]

  • leased:连接池正在使用的连接(Gets the number of persistent connections tracked by the connection manager currently being used to execute requests.The total number of connections in the pool is equal to {@code available} plus {@code leased})
  • pending:等待空闲连接的数量(Gets the number of connection requests being blocked awaiting a free connection. This can happen only if there are more worker threads contending for fewer connections.)
  • available:当前线程池空闲的连接数量(Gets the number idle persistent connections.)
  • max:最大允许的容量
    在这里插入图片描述
    可以看到,leased +available = 池子中的连接数。因为一共四个路由,每个路由最大可以有两个连接,所以一共是8个可用连接数。其中pending表示等待空闲的连接数量。

清除闲置连接evictIdleConnections

关闭闲置2s的连接
HttpClients.custom().evictIdleConnections(TimeValue.ofMilliseconds(2000))

或者自己开一个线程调用poolManager.closeIdle(TimeValue.ofMilliseconds(2000));
最终如下

在这里插入图片描述

删除过期连接 timeToLive 和evictExpiredConnections

timeToLive 的定义:为了复用连接,一般服务器会返回keep alive时间,这样双方就用一个连接发送多个数据。如果太长,就会产生没用的连接,所以需要关闭。
Defines the total span of time connections can be kept alive or execute requests.
timeToLive 推荐不设置值,使用默认即可。同时打开evictExpiredConnections,
这样会使用服务器返回的keepalive,只要在keepalive时间范围内,连接就不会关闭。


设置timetolive值
poolManager.setDefaultConnectionConfig( ConnectionConfig.custom().setTimeToLive(1000, TimeUnit.MILLISECONDS).build());
和打开删除过期的连接HttpClients.custom().evictExpiredConnections()
设置了之后,发现总会存在pending的连接:
在这里插入图片描述
发现是在Closing expired connections时报错,有个方法找不到。
在这里插入图片描述
再引入core包就可以了。

        <dependency>
            <groupId>org.apache.httpcomponents.core5</groupId>
            <artifactId>httpcore5</artifactId>
            <version>5.2.4</version>
        </dependency>

注意释放内存

关闭流

代码中有写到,接response转为字符串返回。其实这个entity.getContent()是一个流,需要被关闭。

  HttpEntity entity = classicHttpResponse.getEntity();
  String s = EntityUtils.toString(entity);

如何关闭?

  1. 手动关闭使用 EntityUtils.consume(entity);
  2. 实现一个HttpClientResponseHandler,就像我们上面中的那样。底层实现里面就已经调用过了。
    在这里插入图片描述

http和netty的关系

http是协议,netty是java的一个NIO的编程框架。比如使用netty我们搭建一个高性能的http 服务器,也可以用netty自定义协议通信。

参考:https://juejin.cn/post/7292029688998068243

Apache HttpClient是一个开源的Java库,用于进行HTTP通信。它提供了一组易于使用的方法和类,用于创建和执行HTTP请求,并处理与服务器之间的通信。 使用Apache HttpClient,您可以发送HTTP请求(如GET、POST、PUT、DELETE),设置请求头,添加请求参数,处理响应,以及处理重定向和认证等功能。它还支持连接池和连接管理器,以便更高效地处理多个请求。 以下是使用Apache HttpClient发送GET请求的简单示例代码: ```java import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.HttpResponse; import org.apache.http.impl.client.HttpClients; public class HttpClientExample { public static void main(String[] args) throws Exception { HttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("https://www.example.com"); HttpResponse response = httpClient.execute(request); int statusCode = response.getStatusLine().getStatusCode(); System.out.println("Status Code: " + statusCode); } } ``` 此示例创建了一个默认的HttpClient实例,并发送了一个GET请求到"https://www.example.com"。然后,它从响应中获取状态码,并将其打印输出。 您可以在项目中引入Apache HttpClient的依赖,以便开始使用它。具体的依赖配置取决于您使用的构建工具(如Maven或Gradle)。 注意:在使用Apache HttpClient进行网络通信时,请确保您遵循适用的法律法规和网站的服务条款,并始终尊重服务器的使用政策。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值