OkHttp 支持 Http
、Socket
代理,可以在初始化OkHttpClient
时,设置客户端指定的代理。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SwitchProxyInterceptor())
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 1080)))
.build();
当用户需要动态指定代理时,可以在初始化参数中添加proxySelector
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SwitchProxyInterceptor())
.proxySelector(new SwitchProxySelector())
.build();
ProxySelector
是JDK
中定义的abstract
类,其中用户需要实现两个函数/** * 根据 URL 返回代理列表 */ public List<Proxy> select(URI uri) ; /** * 代理失败时,可以删除代理等操作 */ public void connectFailed(URI uri, SocketAddress sa, IOException ioe)
虽然ProxySelector
可以根据URL选择代理,但是当需要根据不同的request
选择代理时,ProxySelector
就不能应对这种情况,因为ProxySelector
没办法得知request
的任何信息,更无法知道应该返回何种代理。
这时**ThreadLocal
**就可以解决这一问题。ThreadLocal
是存储当前线程的数据,当需要调用链路特别长时,上游使用ThreadLocal
存储某些信息,下游可以直接拿到,而不用把参数从上游一直传递到下游。
根据这一原理我们可以在OkHttp
发起调用时,设置代理信息,然后ProxySelector
获取本Request
需要返回的代理
实现只需要两个类SwitchProxySelector
和SwitchProxyInterceptor
。
SwitchProxyInterceptor
提取request
设置的代理SwitchProxySelector
返回需要的的代理SwitchProxyInterceptor
SwitchProxyInterceptor
需要检测request
的header
有没有meta.proxy
,然后根据指定的规则往ThreadLocal
中存入proxy
,随后清除meta.proxy
的header
public class SwitchProxyInterceptor implements Interceptor {
private final static Logger logger = LoggerFactory.getLogger(SwitchProxyInterceptor.class);
@NotNull
public Response intercept(@NotNull Chain chain) throws IOException {
if(chain.request().header("meta.proxy")!=null){
String proxyHeader = chain.request().header("meta.proxy");
logger.debug("detect proxy header : {}", proxyHeader);
SwitchProxySelector.proxyThreadLocal.set(SwitchProxySelector.getProxy(proxyHeader));
Request newRequest = chain.request().newBuilder().removeHeader("meta.proxy").build();
return chain.proceed(newRequest);
}
return chain.proceed(chain.request());
}
}
SwitchProxySelector
SwitchProxySelector
提供工厂方法用来解析meta.proxy
中的header
,返回代理
meta.proxy
的模式为{socket|http}:{host}:{port}
public class SwitchProxySelector extends ProxySelector {
private final static Logger logger = LoggerFactory.getLogger(SwitchProxySelector.class);
/**
* 根据request返回
*/
public List<Proxy> select(URI uri) {
Proxy proxy = SwitchProxySelector.proxyThreadLocal.get();
if(proxy==null){
proxy = Proxy.NO_PROXY;
}
logger.debug("{} use proxy {}:{}", uri.toString(), proxy.type().name(), proxy.address());
SwitchProxySelector.proxyThreadLocal.remove();
return Collections.singletonList(proxy);
}
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
}
public static ThreadLocal<Proxy> proxyThreadLocal = new ThreadLocal<>();
/**
* proxy 模式
*/
private static final Pattern PROXY_PATTERN = Pattern.compile("(socket|http):(.*):(.*)");
/**
* 工厂方法 获取Proxy
* @param proxyString meta.proxy 中的 proxy 字符串
* @return Proxy
*/
static Proxy getProxy(String proxyString){
if(proxyString==null || "".equals(proxyString)){
return Proxy.NO_PROXY;
}
Matcher matcher = PROXY_PATTERN.matcher(proxyString);
if(matcher.matches()){
switch (matcher.group(1)){
case "socket":
return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(matcher.group(2), Integer.parseInt(matcher.group(3))));
case "http":
return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(matcher.group(2), Integer.parseInt(matcher.group(3))));
default:
return Proxy.NO_PROXY;
}
}
return Proxy.NO_PROXY;
}
}
public class TestSwitchProxy {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SwitchProxyInterceptor())
.proxySelector(new SwitchProxySelector())
.build();
Request request1 = new Request.Builder().url("https://www.google.com")
.header("meta.proxy", "socket:192.168.0.63:1080")
.build();
Request request2 = new Request.Builder().url("http://www.baidu.com").build();
Request request3 = new Request.Builder().url("https://www.google.com")
.header("meta.proxy", "http:192.168.0.63:8118")
.build();
Callback callback = new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
System.out.println(call.request().url().toString()+" 连接失败");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
System.out.println(call.request().url().toString() +" 连接成功-"+ response.request().url().toString());
}
};
client.newCall(request1).enqueue(callback);
client.newCall(request2).enqueue(callback);
client.newCall(request3).enqueue(callback);
client.newCall(request3).enqueue(callback);
client.newCall(request2).enqueue(callback);
client.newCall(request1).enqueue(callback);
client.newCall(request2).enqueue(callback);
client.newCall(request3).enqueue(callback);
}
}
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxyInterceptor] detect proxy header : http:192.168.0.63:8118
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxyInterceptor] detect proxy header : socket:192.168.0.63:1080
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxyInterceptor] detect proxy header : http:192.168.0.63:8118
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxyInterceptor] detect proxy header : socket:192.168.0.63:1080
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxyInterceptor] detect proxy header : http:192.168.0.63:8118
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] http://www.baidu.com/ use proxy DIRECT:null
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] https://www.google.com/ use proxy SOCKS:/192.168.0.63:1080
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] https://www.google.com/ use proxy SOCKS:/192.168.0.63:1080
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] https://www.google.com/ use proxy HTTP:/192.168.0.63:8118
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] https://www.google.com/ use proxy HTTP:/192.168.0.63:8118
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] http://www.baidu.com/ use proxy DIRECT:null
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] http://www.baidu.com/ use proxy DIRECT:null
[20-12-06 15:23 DEBUG cn.yogehaoren.SwitchProxySelector] https://www.google.com/ use proxy HTTP:/192.168.0.63:8118
http://www.baidu.com/ 连接成功-http://www.baidu.com/
http://www.baidu.com/ 连接成功-http://www.baidu.com/
http://www.baidu.com/ 连接成功-http://www.baidu.com/
https://www.google.com/ 连接成功-https://www.google.com/
https://www.google.com/ 连接成功-https://www.google.com/
https://www.google.com/ 连接成功-https://www.google.com/
https://www.google.com/ 连接成功-https://www.google.com/
https://www.google.com/ 连接成功-https://www.google.com/
根据测试,多线程并发的情况下,proxy代理并不会相互影响。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。