先把入口分清:Java走代理的三条路
程序在本机跑得好好的,请求频率一高就被对方限制,或者业务上需要让请求从不同地区的IP发出去——这时候就得让请求走代理IP。网上教程不少,但很多一上来就贴代码,你抄完了也不知道自己用的是哪种方式,出了问题都不知道去哪查。Java里让请求走代理,入口归纳下来就三个。
第一种,系统属性。用 System.setProperty 设置 http.proxyHost 这组参数,对整个JVM生效,程序里所有走默认方式的HTTP连接都会被带过去。适合临时调试,一行业务代码不用改。
// 必须在程序发出第一个请求之前设置,晚了就不生效
System.setProperty("http.proxyHost", "123.45.67.89");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "123.45.67.89");
System.setProperty("https.proxyPort", "8080");
// 本机地址排除掉,不然本地调试的请求也绕一圈
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
第二种,Proxy对象。new 一个 java.net.Proxy,塞给连接方法,只影响这一个连接,粒度最细。生产代码里最推荐这种,不会干扰JVM里其他组件的连接行为。
第三种,客户端自带配置。HttpClient、OkHttp这类库都在自己的Builder或Config里留了代理入口,能和连接池、认证、重试一起配,正式项目里用得最多。
怎么选?跑个Demo验证IP通不通,系统属性最快;正经业务代码,用Proxy对象或客户端配置,可控性强得多。
场景一:HttpURLConnection,不引依赖的原生写法
小工具、写Demo、或者老项目里不让随便加依赖,用JDK自带的HttpURLConnection就够了。核心就一句:调用 openConnection(proxy) 这个带参数的重载。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class JdkProxyDemo {
public static void main(String[] args) throws Exception {
// IP和端口从代理服务商后台复制
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("123.45.67.89", 8080));
URL url = new URL("http://httpbin.org/get");
// 关键在这一行:带 proxy 参数的重载
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
StringBuilder result = new StringBuilder();
while ((line = reader.readLine()) != null) {
result.append(line);
}
// origin 字段显示的是出口IP,是代理的IP就说明配置生效了
System.out.println(result);
}
conn.disconnect();
}
}
有个坑提醒一下:openConnection() 不带参数走的是直连,很多人配了半天系统属性没生效,就是因为代码里用的是无参版本。验证代理是否生效最简单的办法,就是请求 httpbin.org/get 这类回显接口,看返回JSON里的 origin 是不是你代理的IP。
场景二:Apache HttpClient,账号密码认证这样写
稍微正式点的项目,多数用的是Apache HttpClient。它配代理分两步:设置代理地址,再挂上认证信息。先加依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
认证部分用 CredentialsProvider,这是它家最顺手的写法:
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class HttpClientProxyDemo {
public static void main(String[] args) throws Exception {
HttpHost proxy = new HttpHost("123.45.67.89", 8080, "http");
// 账号密码认证写在这里
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("123.45.67.89", 8080),
new UsernamePasswordCredentials("你的账号", "你的密码"));
try (CloseableHttpClient client = HttpClients.custom()
.setDefaultProxy(proxy)
.setDefaultCredentialsProvider(credsProvider)
.build()) {
HttpGet request = new HttpGet("http://httpbin.org/get");
try (CloseableHttpResponse response = client.execute(request)) {
System.out.println(EntityUtils.toString(response.getEntity()));
}
}
}
}
如果请求返回 407状态码,意思是代理要求认证但你没提供或者写错了。重点检查 AuthScope 里的地址端口是不是和代理完全一致——端口对不上,认证信息是不会被带过去的。
场景三:OkHttp,一个Builder全搞定
新项目和Android端用OkHttp的比较多,它的代理配置最简洁,认证通过一个回调处理:
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
public class OkHttpProxyDemo {
public static void main(String[] args) throws Exception {
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("123.45.67.89", 8080));
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
// 代理要求账号密码时走这个回调
.proxyAuthenticator((route, response) -> {
String credential = Credentials.basic("你的账号", "你的密码");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
})
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("http://httpbin.org/get")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
}
}
三个客户端都过一遍之后你会发现,写法大同小异,差别主要在认证的挂载位置。整理成表格方便对照:
| 客户端 | 代理设置入口 | 账号密码认证 | 适合场景 |
|---|---|---|---|
| HttpURLConnection | openConnection(proxy) | 全局 Authenticator | 小工具、不加依赖 |
| Apache HttpClient | setDefaultProxy() | CredentialsProvider | 老项目、Spring生态 |
| OkHttp | Builder().proxy() | proxyAuthenticator 回调 | 新项目、Android端 |
SOCKS5 和 HTTPS,两个容易踩的坑
SOCKS5的写法比HTTP代理还简单,把 Type 换成 SOCKS、端口换成对应的就行,HttpURLConnection和OkHttp都是同一个套路:
Proxy socksProxy = new Proxy(Proxy.Type.SOCKS,
new InetSocketAddress("123.45.67.89", 1080));
要注意的是SOCKS5带账号密码的情况:JDK原生方式得设置一个全局的 Authenticator,它对整个JVM生效,多组代理用不同账号时会互相覆盖,这种情况建议直接用OkHttp或HttpClient的认证机制,跟着客户端实例走,互不干扰。
java.net.Authenticator.setDefault(new java.net.Authenticator() {
@Override
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication("你的账号", "你的密码".toCharArray());
}
});
HTTPS这边,走HTTP代理时底层会自动用CONNECT方式建隧道,代码上不用特殊处理。但系统属性方式要把 https.proxyHost 和 https.proxyPort 也设上,只设http的那组,HTTPS请求照样直连。另外别为了省事把SSL证书校验关掉,正常配置下代理隧道不影响证书校验流程。
IP从哪来?接入流程和几条实战建议
代码写好了,IP从哪拿?以神龙HTTP为例,流程就三步:开通后在个人中心拿到API提取链接,程序里请求这个链接就能拿到可用的IP和端口列表,然后把IP塞进上面任意一种Proxy对象里用。它的API兼容主流编程语言,文档里带Java示例代码,基本是复制过来改个参数的事。
几条实战里攒下来的经验:
短效IP要配重试逻辑。短效动态IP本身有存活时间,跑到后半段可能失效。请求失败时重新调提取接口获取新的IP再试一次,比死磕一个IP靠谱得多:
// 失败就取新IP重试的简单骨架
for (int i = 0; i < 3; i++) {
Proxy proxy = fetchProxyFromApi(); // 调提取接口拿可用IP
try {
return doGet(targetUrl, proxy);
} catch (IOException e) {
// 当前IP可能已到期,循环继续取新的
}
}
超时一定要设。不设超时的代理请求,遇到失效IP会把整个线程卡住。连接超时给5秒、读取超时给10秒,是比较稳的经验值。
挑服务商看硬指标。神龙HTTP有3000万+正规授权代理IP储备,可用率99.9%,国内三大运营商正规授权,支持HTTP/HTTPS/SOCKS5三种协议。个人中心里还有可视化用量统计和实时监控,IP有没有异常一眼能看出来。接入过程卡住了,技术团队7×24小时在线,响应挺快。
常见问题
Q: 设置了代理但请求还是走的本机IP,怎么排查?
按顺序查三处:代码里是不是调用了不带参数的 openConnection();系统属性是不是在第一个请求发出之后才设置的;用 httpbin.org/get 打印返回的 origin 字段,确认出口IP到底是谁。三处查完,九成的问题都能定位。
Q: HttpURLConnection 怎么加代理的账号密码认证?
JDK原生方式只能通过 Authenticator.setDefault 设置全局认证,写法在上面SOCKS5那节。如果项目里有多组代理账号,更建议换成OkHttp或HttpClient,它们的认证跟着客户端实例走,互不干扰。
Q: 代理IP用一会儿就失效了,怎么办?
短效动态IP按设计就是会到期的,失效前重新调提取接口获取新的IP即可,程序里把重试逻辑做好。如果业务需要长期稳定的入口,直接选长效静态IP产品线,一个IP能用很久,不用频繁处理到期问题。
Q: 高并发场景下有什么要注意的?
第一,HttpClient和OkHttp的客户端实例全局复用一个就够,别每个请求都new,连接池才能发挥作用;第二,提取接口的调用频率留意服务商的并发限制,IP取回来先放进队列,用完了再取;第三,配合神龙HTTP的低延迟高并发提取能力,公开数据采集和AI训练这类大规模任务的IP供给基本不是瓶颈。





