Okhttp DNS缓存失效机制咨询:对接Consul DNS出现SocketTimeout异常
OkHttp DNS缓存失效机制说明
默认失效规则
- OkHttp 所有正式版本默认未启用独立的DNS缓存层,DNS解析结果的缓存完全依赖JVM内置的全局DNS缓存实现:
- 未配置安全管理器的JVM环境,默认缓存解析成功的DNS结果30秒,解析失败的结果默认缓存10秒
- 配置了安全管理器的JVM环境,默认会永久缓存解析成功的DNS结果
- JVM DNS缓存仅会在TTL到期后自动失效,不会因为对应IP的后端实例不可用主动触发缓存淘汰
- 除DNS层面的缓存外,OkHttp内置的连接池默认会保留空闲TCP连接5分钟,哪怕对应IP的实例已经下线,空闲期内的连接仍会被优先复用,直到读写超时才会被标记为不可用,这也是触发
java.net.SocketTimeoutException的常见诱因
注意:OkHttp 4.7+ 提供了可选的
DnsCache扩展接口,自行实现后可单独配置DNS缓存规则,无需修改全局JVM参数
适配Consul DNS短TTL场景的优化方案
针对Consul这类服务发现DNS TTL短、实例IP频繁变更的场景,可通过以下配置解决缓存导致的请求失败问题:
- 调整JVM全局DNS缓存TTL,和Consul返回的服务DNS TTL对齐,在JVM启动参数中添加以下配置即可:
-Dnetworkaddress.cache.ttl=2 -Dnetworkaddress.cache.negative.ttl=0
上述配置表示成功解析的DNS结果仅缓存2秒,解析失败的结果不做缓存
- 自定义OkHttp DNS解析实现,绕过JVM全局缓存,示例代码如下:
// 自定义DNS实现,每次请求强制发起DNS查询 public class CustomConsulDns implements Dns { @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { // 也可直接对接Consul HTTP接口拉取服务实例列表,跳过DNS解析流程 return Dns.SYSTEM.lookup(hostname); } } // 构建OkHttpClient时注入自定义DNS实现 OkHttpClient okHttpClient = new OkHttpClient.Builder() .dns(new CustomConsulDns()) .build();
- 缩短连接池空闲存活时间,避免复用已经下线实例的连接:
// 配置连接池空闲连接最长存活时间为2秒,最大空闲连接数为50 OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectionPool(new ConnectionPool(50, 2, TimeUnit.SECONDS)) .build();
- 开启连接失败自动重试:
OkHttpClient okHttpClient = new OkHttpClient.Builder() .retryOnConnectionFailure(true) .build();
开启后请求遇到连接异常时,会自动重新解析DNS获取最新实例IP发起重试,降低单实例故障导致的请求失败概率
内容的提问来源于stack exchange,提问作者nicolattu
相关产品推荐
相关产品推荐

