HttpURLConnection使用http代理

工程中的网络请求,有时会需要使用http代理,比较简单的方法是使用apache的httpclient 直接设置代理,但有的时候使用java自带的HttpURLConnection的时候,就需要注意多线程的问题了。 使用HttpURLConnection 实现代理的方法也很简单,在建立连接之前先设置代理:

Authenticator.setDefault(authenticator);

需要注意的是,设置代理的方法并不是使用HttpURLConnection的一个方法,而在建立请求的时候,也没有任何调用和使用 Authenticator的地方,可以猜测这里设置了代理是使用了全局量,跟进Authenticator中去,会发现: 1 2 其实setDefault 方法就是设置了一个静态变量,而这个变量被使用的地方在: 3 (三个同名函数,相同的处理)这个静态变量被全局的网络请求所使用,而不是当前连接独占的配置,一般来说,当前网络使用一个http代理的时候没有问题(比如我们就是通过elb代理多个IP出口),但是当我们有多个代理的时候,在多线程环境下就会出现问题,如果代理服务器的账号密码不同,请求的服务球对cookie和ip进行校验的时候,就会比较麻烦,所以需要想办法来让每一个HttpURLConnection独占这个代理配置,直接的方法似乎没有,但是可以折中,同步网络请求过程中,HttpURLConnection是和唯一线程绑定的,我们可以用ThreadLocal,让每个线程独占一个代理配置,从而间接的保证每个HttpURLConnection始终使用一个代理配置。 可以定一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ThreadLocalAuthenticator extends Authenticator {

ThreadLocal<PasswordAuthentication> auth = new ThreadLocal<>();

public void setPasswordAuthentication(PasswordAuthentication passwordAuthentication) {
auth.set(passwordAuthentication);
}

public void clearPasswordAuthentication() {
auth.remove();
}

@Override
protected PasswordAuthentication getPasswordAuthentication() {
return auth.get();
}
}

然后在http网络请求的工具类中定义一个全局的静态ThreadLocalAuthenticator的实例:

1
private static final ThreadLocalAuthenticator authenticator = new ThreadLocalAuthenticator();

然后在需要的时候使用它就OK了。