Skip to content

整合 okhttp ​

在现代 Java 应用开发中,高效的 HTTP 客户端管理至关重要。本文将介绍如何在 tio-boot 框架中整合 OkHttp,以及几种常见的 OkHttpClient 管理方式,包括使用 Bean 容器管理、单例枚举模式以及设置代理的方法。


添加依赖 ​

xml
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>4.12.0</version>
    </dependency>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp-brotli</artifactId>
      <version>4.12.0</version>
    </dependency>

将 OkHttpClient 添加到 Bean 容器 ​

在 tio-boot 中集成 OkHttpClient 非常简单。只需通过一个配置类将 okhttp3.OkHttpClient 实例加入到容器中即可。以下是一个示例配置:

java
import java.util.concurrent.TimeUnit;

import nexus.io.annotation.ABean;
import nexus.io.annotation.AConfiguration;

import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;

@AConfiguration
public class OkHttpClientConfig {

  @ABean
  public OkHttpClient config() {
    Builder builder = new OkHttpClient().newBuilder();
    // 配置连接池
    builder.connectionPool(pool());
    // 设置连接超时和读取超时
    builder.connectTimeout(120L, TimeUnit.SECONDS)
           .readTimeout(120L, TimeUnit.SECONDS);
    return builder.build();
  }

  private ConnectionPool pool() {
    return new ConnectionPool(200, 5, TimeUnit.MINUTES);
  }
}

为什么要将 OkHttpClient 添加到 Bean 容器 ​

将 OkHttpClient 实例放入容器中有多个好处,尤其在开发大型应用时更为重要:

  1. 重用连接和线程池:OkHttpClient 内部使用连接池和线程池来优化资源利用和提升性能。通过将其作为单例或有限实例放入容器,可确保整个应用共享这些资源,避免重复创建和销毁带来的开销,从而降低内存占用并提高响应速度。

  2. 统一配置:将 OkHttpClient 配置集中管理,可以统一设置超时、连接池大小等参数。这样不仅便于维护和调整配置,还能确保应用中所有 HTTP 请求行为一致,适用于不同环境(开发、测试、生产)的统一管理。

  3. 便于维护和测试:通过依赖注入管理 OkHttpClient,可以在不同环境或测试场景中轻松替换或模拟 HTTP 客户端。这样在编写单元测试和集成测试时,无需改动业务代码即可使用模拟的 HTTP 行为,提高测试效率和灵活性。

  4. 减少资源泄漏风险:容器管理 OkHttpClient 的生命周期,有助于防止资源泄漏(如未及时关闭连接)。框架自动管理资源,可以有效降低由于不当使用 API 而导致的问题。

  5. 提高开发效率:由框架自动管理依赖项后,开发者可以专注于业务逻辑,而不必耗费精力管理底层 HTTP 通信细节,从而提高开发效率。


单例枚举模式管理 OkHttpClient ​

除了通过 Bean 容器管理 OkHttpClient,还可以使用单例枚举模式进行管理。这种方式不依赖 Bean 容器,简单且线程安全。以下是一个示例:

java
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;

public enum OkHttpClientPool {
  INSTANCE;

  static okhttp3.OkHttpClient.Builder builder;
  static {
    builder = new OkHttpClient().newBuilder();
    // 配置连接池
    builder.connectionPool(pool());
    // 信任所有连接
    builder.sslSocketFactory(sslSocketFactory(), x509TrustManager());
    // 设置连接超时和读取超时
    builder.connectTimeout(120L, TimeUnit.SECONDS)
           .readTimeout(120L, TimeUnit.SECONDS);
  }

  public static OkHttpClient getHttpClient() {
    return builder.build();
  }

  private static ConnectionPool pool() {
    return new ConnectionPool(200, 5, TimeUnit.MINUTES);
  }

  public static X509TrustManager x509TrustManager() {
    return new X509TrustManager() {
      @Override
      public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { }

      @Override
      public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { }

      @Override
      public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
      }
    };
  }

  public static SSLSocketFactory sslSocketFactory() {
    try {
      // 配置信任所有证书的 SSL 上下文
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(null, new TrustManager[] { x509TrustManager() }, new SecureRandom());
      return sslContext.getSocketFactory();
    } catch (NoSuchAlgorithmException | KeyManagementException e) {
      e.printStackTrace();
    }
    return null;
  }
}

通过 OkHttpClientPool.INSTANCE.getHttpClient() 即可获取配置好的 OkHttpClient 实例。该模式简单、易于使用且无需依赖框架的容器管理。


使用 tio-utils 的 OkHttpClientPool ​

如果正在使用 tio-utils 工具集,可以直接调用其封装好的 OkHttpClientPool 方法,例如:

java
import nexus.io.tio.utils.http.OkHttpClientPool;

OkHttpClient client = OkHttpClientPool.get120HttpClient();

这将返回一个已经配置好连接超时、连接池等参数的 OkHttpClient 实例,简化了客户端的创建过程。


设置代理 ​

在实际开发中,有时需要通过代理服务器进行 HTTP 请求。以下是一个示例,演示如何在 OkHttp 中配置代理及其认证:

java
package nexus.io.spider.rmp.client;

import java.net.InetSocketAddress;
import java.net.Proxy;

import nexus.io.tio.utils.environment.EnvUtils;

import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

public class MyHttpClient {

  // 从环境变量中读取代理配置信息
  private static final String PROXY_HOST = EnvUtils.getStr("PROXY_HOST");
  private static final int PROXY_PORT = EnvUtils.getInt("PROXY_PORT");
  private static final String PROXY_USERNAME = EnvUtils.getStr("PROXY_USERNAME");
  private static final String PROXY_PASSWORD = EnvUtils.getStr("PROXY_PASSWORD");

  // 使用代理的 OkHttpClient 实例
  public static final OkHttpClient httpClient = createHttpClientWithProxy();

  // 创建带有代理和认证的 OkHttpClient 实例
  private static OkHttpClient createHttpClientWithProxy() {
    // 配置代理服务器
    Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));

    // 设置代理认证器
    Authenticator proxyAuthenticator = new Authenticator() {
      @Override
      public Request authenticate(Route route, Response response) {
        String credential = Credentials.basic(PROXY_USERNAME, PROXY_PASSWORD);
        return response.request().newBuilder()
                       .header("Proxy-Authorization", credential)
                       .build();
      }
    };

    return new OkHttpClient.Builder()
             .proxy(proxy)
             .proxyAuthenticator(proxyAuthenticator)
             .build();
  }
}

通过上述代码,可以轻松配置 OkHttpClient 使用 HTTP 代理,并提供代理认证信息。EnvUtils 用于从环境变量中读取代理的主机、端口、用户名和密码,使配置更加灵活和安全。


总结来说,将 OkHttpClient 集成进的应用框架时,有多种管理方式可选。使用 Bean 容器管理可以充分利用依赖注入的优势,而单例枚举模式则提供了一种简单、无需外部依赖的实现方法。无论哪种方式,都能帮助更高效地管理 HTTP 客户端资源,并根据需要轻松配置代理等功能。

MIT Licensed