HttpUtils工具类(二)Apache HttpClient 5 使用详细教程

时间:2025-02-22 08:05:10

        

目录

一、Apache HttpClient 5介绍

(1)核心特性

(2)Apache HttpClient 5 的新特性

(3)在 Java 项目的主要使用场景及缺点

使用场景:

缺点:

二、在实际项目中的应用

(1)引入maven配置

(2)自定义HttpUtils工具类---实现连接管理、重试策略等

功能概述

(3)代码中的具体实现

1、Get请求

2、POST请求-常规

3、POST请求-上传文件

(4)高级用法

1、处理重定向

2、SSL/TLS 支持

3、处理异步请求

4、处理 Cookie

5、HTTPDNS支持


一、Apache HttpClient 5介绍

Apache HttpClient 5 是一个功能齐全且高度可定制的 HTTP 客户端库, 其专门用于发送 HTTP 请求、处理 HTTP 响应并支持各种 HTTP 协议特性。特别适合处理复杂的 HTTP 请求需求,如多协议支持、认证、连接池、代理等。它适合中大型项目或需要高级 HTTP 特性的应用开发。

(1)核心特性

  1. 功能强大:

    • 同步与异步支持:支持同步和异步的 HTTP 请求处理,异步请求在处理大量并发请求时能够显著提高效率。
    • 连接池管理:内置的连接池机制能提高并发处理性能,减少资源消耗。
    • 多种认证机制:支持多种认证方式,包括 Basic、Digest、NTLM、Kerberos 等,能够处理多种安全场景。
    • 支持Cookie管理:内置 Cookie 管理功能,能够自动处理服务端返回的 Cookie 并在后续请求中使用,模拟浏览器行为。
  2. 协议支持广泛:

    • HTTP/1.1 和 HTTP/2 支持:支持现代 HTTP 协议,包括 HTTP/2 的多路复用和流优先级等特性,提升了网络请求效率;特别是 HTTP/2 多路复用的优势。
    • SSL/TLS 支持:支持 HTTPS,提供自定义 SSL/TLS 配置,确保通信的安全性。HttpClient 5 可以方便地处理 HTTPS 请求,支持定制化的 SSL/TLS 配置。
  3. 灵活性和可扩展性:

    • 易于扩展和定制:允许开发者根据需要进行灵活的定制,HttpClient 5 的设计高度模块化,用户可以根据需要对连接管理、重定向策略、请求重试策略、代理设置等进行灵活定制。
    • 代理支持:可以轻松配置 HTTP 或 SOCKS 代理,用于跨网络访问和隐私保护。
  4. 健壮的错误处理机制:

    • 自动重试和重定向处理:内置的重试机制和自动重定向处理,可以减少由于网络问题导致的失败请求。开发者可以定制是否启用自动重定向。

(2)Apache HttpClient 5 的新特性

与之前的 版本相比,HttpClient 5 进行了大量的改进和优化,特别是在性能和安全性方面:

  • HTTP/2 支持:提供了完整的 HTTP/2 支持,包括多路复用、流优先级等特性。
  • 更好的异步支持:在处理并发请求时,通过异步模型极大提升了响应速度和吞吐量。
  • 灵活的响应处理:通过改进的响应处理 API,可以更方便地处理大型响应体,避免内存溢出问题。

(3)在 Java 项目的主要使用场景及缺点

使用场景:

  1. RESTful API 客户端:与第三方 API 交互,发送各种 HTTP 请求。
  2. Web 爬虫:抓取网页内容,处理重定向、Cookie 等。
  3. 分布式系统通信:用于微服务间的 HTTP 通信。
  4. 测试与自动化:模拟 HTTP 请求,进行集成和自动化测试。
  5. 代理与网关:处理请求代理和认证机制。
  6. 文件上传下载:实现大文件的传输。
  7. 认证交互:处理 OAuth2、JWT 等认证协议。
  8. 安全通信:处理 HTTPS 请求,确保数据安全。

缺点:

  1. 学习曲线陡峭:高级特性(如自定义连接池、SSL 配置、异步请求等)较为复杂,新手需要时间学习和掌握。
  2. 体积较大:相比轻量级客户端,如 OkHttp,HttpClient 依赖库较多,可能不适合小型项目。
  3. 性能消耗高:默认配置下的内存和 CPU 占用较高,需调整才能达到最佳性能。
  4. 配置繁琐:高级定制(如连接管理、认证、代理)需要较多配置,增加开发复杂度。
  5. 异步编程复杂:异步请求的回调、错误处理等逻辑复杂,增加代码难度。

二、在实际项目中的应用

(1)引入maven配置

首先,你需要将 HttpClient 5 的依赖加入到项目中。 文件如下:

<dependency>
    <groupId>.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1</version>
</dependency>

(2)自定义HttpUtils工具类---实现连接管理、重试策略等

import .slf4j.Slf4j;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import .;
import ;

import ;
import ;
import ;
import ;
import ;
import ;


/**
 * @author 响叮当
 * @since 2024/8/15 14:10
 **/
@Slf4j
@Component
public class ApacheHttpClientUtil {

    private CloseableHttpClient httpClient;

    @PostConstruct
    public void init() {
        SocketConfig socketConfig = ()
                .setSoTimeout((1000))
                .build();

        PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder
                .create()
                .setDefaultSocketConfig(socketConfig)
                .setMaxConnTotal(1000)
                .setMaxConnPerRoute(50)
                .build();

        RequestConfig requestConfig = ()
                .setConnectionRequestTimeout((8000))
                .setResponseTimeout((8000))
                .setConnectTimeout((8000))
                .build();

        httpClient = HttpClients
                .custom()
                .disableContentCompression()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }


    public CloseableHttpResponse getOrHead(String url, String method, Map<String, String> headers) throws IOException {
        HttpUriRequestBase request = new HttpUriRequestBase(method, (url));
        BasicHeader[] head = mapToHeaders(headers);
        (head);
        return (request);
    }


    public CloseableHttpResponse post(String url, Map<String, String> headers, HttpEntity httpEntity) throws IOException {
        HttpPost request = new HttpPost(url);
        BasicHeader[] head = mapToHeaders(headers);
        (head);
        (httpEntity);
        return (request);
    }

    public static BasicHeader[] mapToHeaders(Map<String, String> map) {
        BasicHeader[] headers = new BasicHeader[()];
        int i = 0;
        for (<String, String> entry : ()) {
            headers[i++] = new BasicHeader((), ());
        }
        return headers;
    }

    public static void closeQuietly(Closeable is) {
        if (is != null) {
            try {
                ();
            } catch (Exception ex) {
                ("Resources encounter an exception when closing,ex:{}", ());
            }
        }
    }

}

功能概述

  1. 初始化 HTTP 客户端init() 方法初始化 CloseableHttpClient,配置连接池、请求超时和套接字超时。
    • 连接池配置:通过 PoolingHttpClientConnectionManagerBuilder 设置最大连接数(1000)和每路由的最大连接数(50),用于高并发场景。
    • 请求配置:包括连接超时、请求超时和响应超时,确保在合理的时间内处理请求。
  2. HTTP 请求处理
    • GET/HEAD 请求getOrHead() 方法可发送 GET 或 HEAD 请求,并接受自定义请求头。
    • POST 请求post() 方法可发送 POST 请求,支持自定义请求头和实体。
  3. 资源管理closeQuietly() 方法用于关闭资源,避免抛出异常。

(3)代码中的具体实现

1、Get请求

   @GetMapping("/get")
   public void testGet() {
        String url = "";
        try {
            // 1、构建入参、添加 headers
            Map<String, String> headers = new HashMap<>();

            // 2、发起http请求
            CloseableHttpResponse httpResponse = (url, "GET", headers);

            // 3、返回结果,异常处理
            if (() == 200) {
                String resStr = (());
                ("request_success, response:{}, httpResponse_Code:{}, reasonPhrase:{}", resStr, (), ());
            } else {
                ("request_fail, httpResponse_Code:{}, reasonPhrase:{}", (), ());
            }
        } catch (Exception e) {
            ("request_udmp_fail, ex:{}", e);
        }
    }

2、POST请求-常规

    @PostMapping("/post")
    public void testPost(@RequestBody PostReq req) {
        String url = "";
        try {
            // 1、构建入参、添加 headers
            StringEntity stringEntity = new StringEntity((req), ContentType.APPLICATION_JSON);
            Map<String, String> headers = new HashMap<>();

            // 2、发起http请求
            CloseableHttpResponse httpResponse = (url, headers, stringEntity);

            // 3、返回结果,异常处理
            if (() == 200) {
                String resStr = (());
                ("request_success, response:{}, httpResponse_Code:{}, reasonPhrase:{}", resStr, (), ());
            } else {
                ("request_fail, httpResponse_Code:{}, reasonPhrase:{}", (), ());
            }
        } catch (Exception e) {
            ("request_udmp_fail, ex:{}", e);
        }
    }

3、POST请求-上传文件

    /**
     * 上传文件
     * @param multipartFile
     * @param token
     * @param key
     * @return
     */
    @PostMapping("/uploadFile")
    public UploadRes testPostFile(
            @RequestParam("file") MultipartFile multipartFile,
            @RequestParam("token") String token,
            @RequestParam("key") String key
    ) {
        UploadRes res = null;
        String url = "";
        try {
            // 1、构建File参数
            File file = new File(());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                (());
            }
            MultipartEntityBuilder builder = ();
            ("file", file, ContentType.MULTIPART_FORM_DATA, "");

            // 2、构建其他参数
            ("token", token, ContentType.TEXT_PLAIN);
            ("key", key, ContentType.TEXT_PLAIN);
            HttpEntity multipartEntity = ();

            // 3、需要加 header,在这里加
            Map<String, String> headers = new HashMap<>();

            // 4、发起http请求
            CloseableHttpResponse httpResponse = (url, headers, multipartEntity);

            // 5、返回结果,异常处理
            if (() == 200) {
                String resStr = (());
                res = (resStr, );
                ("request_success, response:{}, httpResponse_Code:{}, reasonPhrase:{}", resStr, (), ());
            } else {
                ("request_fail, httpResponse_Code:{}, reasonPhrase:{}", (), ());
            }
        } catch (Exception e) {
            ("request_udmp_fail, ex:{}", e);
        }
        return res;
    }

(4)高级用法

1、处理重定向

HttpClient 5 默认会处理 3XX 重定向,但你也可以自定义行为。

CloseableHttpClient httpClient = ()
    .disableRedirectHandling()  // 禁用自动重定向
    .build();

2、SSL/TLS 支持

使用 HttpClient 5 可以轻松处理 HTTPS 请求,下面展示如何自定义 SSL 配置。

import .;
import .;
import ;

SSLContext sslContext = ()
    .loadTrustMaterial((chain, authType) -> true)  // 信任所有证书
    .build();

CloseableHttpClient httpClient = ()
    .setSSLContext(sslContext)
    .build();

3、处理异步请求

如果你需要发送异步 HTTP 请求,可以使用 HttpAsyncClient

import .;
import .;
import .;
import .;

CloseableHttpAsyncClient asyncClient = ();

();

HttpGet request = new HttpGet("/posts/1");

(request, new FutureCallback<>() {
    @Override
    public void completed(CloseableHttpResponse response) {
        ("Response received: " + ());
    }

    @Override
    public void failed(Exception ex) {
        ("Request failed: " + ());
    }

    @Override
    public void cancelled() {
        ("Request cancelled");
    }
});

4、处理 Cookie

import .;
import .;
import .;

CookieStore cookieStore = new BasicCookieStore();
CloseableHttpClient httpClient = ()
    .setDefaultCookieStore(cookieStore)
    .build();

5、HTTPDNS支持

    // 当域名为结尾时候,转到local的机器上
    String Target_IP= "127.0.0.1";
    String[] domains = new String[]{""};

    DnsResolver dnsResolver = new DnsResolver() {
        @Override
        public InetAddress[] resolve(String host) throws UnknownHostException {
            if ((domains[0]))) {
                return new InetAddress[]{(Target_IP)};
            }
            return new InetAddress[0];
        }

        @Override
        public String resolveCanonicalHostname(String s) {
            return null;
        }
    };


    // @PostConstruct
    public void init() {
        SocketConfig socketConfig = ()
                .setSoTimeout((1000))
                .build();

        PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder
                .create()
                .setDnsResolver(dnsResolver) // 这里是HttpDns配置
                .setDefaultSocketConfig(socketConfig)
                .setMaxConnTotal(1000)
                .setMaxConnPerRoute(50)
                .build();