OkHttp配置域名IP绑定的核心思路,就是通过自定义Dns接口,让指定域名在解析时直接返回你预设的固定IP,从而绕过系统DNS解析,适合测试联调、灰度发布和本地开发场景。
OkHttp为什么需要单独配置域名IP绑定
日常开发中,你大概率遇到过这几种情况:测试服务器还没正式接入域名解析,但客户端代码里已经写死了正式域名;灰度发布时要让部分流量打到新服务器,又不想改代码里的请求地址;本地调试时公司内网DNS抽风,域名解析半天出不来。
这些场景的共同痛点是:域名没法正常解析到目标IP,但改请求URL又太麻烦。
OkHttp作为Java和Android端使用最广泛的HTTP客户端之一,它的网络请求流程里预设了一个可以拦截DNS解析的扩展点,只要在这个扩展点上做文章,就能在不改动业务请求代码的前提下,把域名和IP的关系固定下来。
行业共识认为,客户端网络请求的域名解析环节,是排查连接问题和优化请求耗时的关键切入点之一,理解了这一层,配置域名IP绑定就不再是玄学操作。
OkHttp指定IP访问域名的原理:自定义Dns接口
OkHttp在发起请求前,会调用内部的一个Dns接口实现,把URL中的域名转换成IP地址,默认实现是Dns.SYSTEM,它走的是系统底层的DNS解析服务。
这个Dns接口长这样:
public interface Dns {
List<InetAddress> lookup(String hostname) throws UnknownHostException;
}
lookup方法接收一个域名,返回一组IP地址列表,OkHttp拿到这个列表后,会尝试逐个连接。
OkHttp域名绑定IP的本质,就是替换这个Dns实现,你可以在lookup方法里加一个域名到IP的映射表,命中映射表就直接返回固定IP,没命中再交给Dns.SYSTEM走正常解析。
这样做的好处是:请求URL中的域名完全不变,Host头也不会变,只有底层的IP寻址被改变了,对于服务端来说,它看到的仍然是一个正常的域名请求。
安卓OkHttp域名解析到指定IP的完整配置步骤
下面按操作顺序,把每一步讲清楚,Java和Kotlin写法基本一致,这里用Java演示。
第一步:创建自定义Dns实现类
新建一个类,实现okhttp3.Dns接口:
import okhttp3.Dns; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; importjava.util.HashMap; import java.util.List; import java.util.Map; public class FixedIpDns implements Dns { private final Map<String, List<InetAddress>> ipMap = new HashMap<>(); public FixedIpDns() { try { // 这里填写你要绑定的域名和对应IP ipMap.put("api.example.com", Arrays.asList(InetAddress.getByName("192.168.1.100"))); ipMap.put("cdn.example.com", Arrays.asList(InetAddress.getByName("10.0.0.20"))); } catch (UnknownHostException e) { // IP地址格式错误时会走到这里 e.printStackTrace(); } } @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { List<InetAddress> fixedIp = ipMap.get(hostname); if (fixedIp != null && !fixedIp.isEmpty()) { return fixedIp; } // 未绑定的域名走系统默认DNS解析 return Dns.SYSTEM.lookup(hostname); } }
这个类的核心就两点:一是构造时把域名和IP的对应关系塞进Map,二是lookup方法里优先查这个Map,查不到就回退到系统解析。
第二步:构建OkHttpClient时传入自定义Dns
创建OkHttpClient实例时,通过Builder的dns()方法替换默认实现:
OkHttpClient client = new OkHttpClient.Builder()
.dns(new FixedIpDns())
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
之后所有用这个client发起的请求,都会自动应用你配置的域名IP绑定规则。
第三步:正常发起请求,URL保持不变
业务代码完全不用改,请求地址还是原来的域名:
Request request = new Request.Builder()
.url("https://api.example.com/user/info")
.get()
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 处理失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 处理响应
}
});
OkHttp在内部会自动调用你的FixedIpDns.lookup("api.example.com"),拿到168.1.100这个IP去建立连接。
OkHttp绕过DNS解析后HTTPS证书和Host头的处理
很多人配置完域名IP绑定后,发现HTTP请求通了,HTTPS请求却报证书错误,这几乎是必踩的坑。
原因很简单:HTTPS证书是颁发给域名的,证书里写的是
api.example.com,但实际连接的IP是168.1.100,OkHttp发现证书上的域名和连接目标对不上,就会触发证书校验失败。
处理方式有两种,按安全等级从高到低排列:
- 服务端使用包含IP地址的证书:在证书的SAN扩展里同时加入域名和IP,这样无论用域名还是IP访问,证书都能通过校验,这是最规范的方式。
- 客户端临时跳过证书校验:开发测试阶段可以自定义
HostnameVerifier,让所有主机都信任,但生产环境绝对不能这么做,会带来中间人攻击风险。
临时跳过证书校验的代码示例:
OkHttpClient client = new OkHttpClient.Builder()
.dns(new FixedIpDns())
.hostnameVerifier((hostname, session) -> true)
.build();
Host头的问题不用太担心,OkHttp发送请求时,Host头取的是URL中的域名,不是解析后的IP,所以服务端如果按Host做虚拟主机路由,配置域名IP绑定后不会影响正常路由识别。
三种域名IP绑定方案对比:OkHttp自定义Dns、系统hosts、修改请求URL
实际开发中实现“域名绑定IP”不止一种办法,下面用表格对比三种常见方案的差异,帮你快速选型。
| 对比项 | OkHttp自定义Dns | 修改系统hosts文件 | 直接改请求URL为IP |
|---|---|---|---|
| 代码侵入性 | 低,只在初始化时配置 | 无代码改动 | 高,要改业务请求逻辑 |
| 生效范围 | 当前OkHttpClient实例 | 整个系统所有请求 | 当前请求 |
| Host头保持域名 | 是 | 是 | 否,Host会变成IP |
| 便于动态切换 | 高,可随时改Map | 低,需重启或刷新DNS | 低,需改代码 |
| HTTPS证书适配 | 需额外处理 | 需额外处理 | 需额外处理 |
| Android设备可用性 | 高,无需root | 低,多数手机无法直接改hosts | 高,但不适合生产 |
从表格能看出来,OkHttp自定义Dns方案在灵活性和可控性上优势明显,尤其适合安卓App内需要临时切换服务器IP的场景,系统hosts适合本地电脑调试,修改请求URL则是最不推荐的做法,因为它会破坏Host头,导致很多依赖域名路由的服务端无法正常工作。
配置OkHttp域名IP绑定时的几个实操细节
IP地址格式要写对
InetAddress.getByName()方法接收的是一个字符串IP,比如"192.168.1.100"或"10.0.0.20",不要写"http://192.168.1.100"或带端口号的地址,否则会抛UnknownHostException,端口号和路径是URL里的事,和DNS解析无关。
域名要区分大小写
lookup方法收到的域名通常是完整的主机名,比如api.example.com,不包含斜杠和端口,映射表的key要和URL中的域名完全一致,包括大小写,OkHttp默认不会对域名做大小写转换,所以api.Example.com和api.example.com会被当成两个不同的key。
多个IP可以同时绑定
Android的OkHttp在某些版本里会尝试连接返回列表中的第一个可用IP,如果你想做简单的负载均衡或故障切换,可以给一个域名绑定多个IP:
ipMap.put("api.example.com", Arrays.asList(
InetAddress.getByName("192.168.1.100"),
InetAddress.getByName("192.168.1.101")
));
但要注意,OkHttp对列表中的IP选择策略并不保证轮询,具体行为取决于版本和系统实现,生产环境如果需要可靠的负载均衡,还是建议用专业方案。
Q&A:OkHttp域名绑定IP怎么设置相关问题
OkHttp域名绑定IP怎么设置才能不影响HTTPS证书校验?
正确做法是让服务端证书同时包含域名和IP的SAN扩展,开发测试阶段可临时实现HostnameVerifier返回true跳过校验,但上线前必须移除,OkHttp官方文档明确不建议在生产环境关闭主机名校验。
OkHttp设置域名IP后,请求头里的Host会变吗?
不会,Host头取的是请求URL中的域名部分,与DNS解析结果无关,所以配置域名IP绑定后,服务端接收到的Host依然是原始域名,虚拟主机路由不会受影响。
除了自定义Dns,还有什么办法实现OkHttp绕过DNS解析?
还可以通过拦截器把请求URL中的域名替换成IP,同时手动设置Host头保持域名,但这种做法改动请求地址和请求头,容易引入新问题,代码复杂度也更高,相较之下,自定义Dns是OkHttp官方推荐且侵入性最低的方式。
配置OkHttp域名IP绑定并不复杂,掌握自定义Dns接口这一个核心点就能灵活应对大多数场景,只要注意HTTPS证书校验和Host头这两个细节,就能在测试联调和灰度发布中省下大量改代码和改环境配置的时间。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/644844.html





