You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程环境下JSoup代理认证及多实例不同代理配置咨询

为多个JSoup实例配置独立代理的解决方案

首先得戳破核心问题:你遇到的困境根源在于**System.setProperty是全局生效的**,多线程/多实例场景下必然互相干扰;而JSoup原生的.proxy(ip, port)只负责设置代理地址,认证信息依然绑定全局系统属性,这就是你分开设置无效的原因。下面给你两个实用的实现思路,按推荐程度排序:

方案一:用OkHttpClient作为JSoup的底层客户端(强推)

OkHttpClient天生支持为每个请求单独配置代理及认证,完全绕开全局设置的坑,步骤非常清晰:

  1. 先引入OkHttpClient依赖(以Maven为例):
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version> <!-- 替换成最新稳定版即可 -->
</dependency>
  1. 为每个代理创建独立的OkHttpClient实例:
    比如配置两个不同的代理客户端:
// 第一个代理的配置
String proxyHost1 = "192.168.1.100";
int proxyPort1 = 8080;
String proxyUser1 = "proxy_user_1";
String proxyPass1 = "proxy_pass_1";

// 构建代理认证逻辑
Authenticator proxyAuth1 = (route, response) -> {
    String credential = Credentials.basic(proxyUser1, proxyPass1);
    return response.request().newBuilder()
            .header("Proxy-Authorization", credential)
            .build();
};

// 创建带专属代理的OkHttpClient
OkHttpClient client1 = new OkHttpClient.Builder()
        .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost1, proxyPort1)))
        .proxyAuthenticator(proxyAuth1)
        .build();

// 同理创建第二个代理的client2...
  1. 在JSoup请求时指定对应的客户端:
// 使用第一个代理请求目标页面
Document doc1 = Jsoup.connect("https://example.com")
        .client(client1)
        .get();

// 使用第二个代理请求同一页面
Document doc2 = Jsoup.connect("https://example.com")
        .client(client2)
        .get();

这种方式的优势是完全隔离每个请求的代理配置,不管是多线程并发还是单线程多实例切换,都不会出现串用代理的问题,而且OkHttpClient的性能、重试机制等都比JSoup原生客户端更强大。

方案二:用ThreadLocal隔离全局Authenticator(适合不想加依赖的场景)

如果不想引入额外依赖,可以用ThreadLocal绑定当前线程的代理认证信息,让全局Authenticator动态读取对应线程的配置:

  1. 定义ThreadLocal存储当前线程的认证凭证:
private static final ThreadLocal<String> PROXY_CREDENTIALS = new ThreadLocal<>();
  1. 设置全局Authenticator,从ThreadLocal中读取凭证:
Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        String credentials = PROXY_CREDENTIALS.get();
        if (credentials != null) {
            String[] userPass = credentials.split(":", 2);
            return new PasswordAuthentication(userPass[0], userPass[1].toCharArray());
        }
        return super.getPasswordAuthentication();
    }
});
  1. 每次请求前设置当前线程的认证信息,配合JSoup的.proxy()方法:
// 第一个代理请求流程
PROXY_CREDENTIALS.set("proxy_user_1:proxy_pass_1");
Document doc1 = Jsoup.connect("https://example.com")
        .proxy("192.168.1.100", 8080)
        .get();
PROXY_CREDENTIALS.remove(); // 务必清理,避免污染后续线程

// 第二个代理请求流程
PROXY_CREDENTIALS.set("proxy_user_2:proxy_pass_2");
Document doc2 = Jsoup.connect("https://example.com")
        .proxy("10.0.0.50", 8888)
        .get();
PROXY_CREDENTIALS.remove();

⚠️ 注意:必须在请求完成后调用remove()清理ThreadLocal的值,否则线程池中的复用线程会携带旧的认证信息,导致代理串用。

为什么你之前的尝试无效?

你之前只设置System.setProperty("http.proxyUser")和http.proxyPassword,再用.proxy()指定IP端口,本质上是JSoup原生客户端优先读取系统属性的代理地址,当你调用.proxy()时虽然覆盖了地址,但认证信息还是从全局系统属性中读取——如果多个线程同时修改系统属性,必然出现配置混乱,自然无法生效。

内容的提问来源于stack exchange,提问作者A. Pecora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:47:18