背景
因为业务需求导致服务迁移,迁移过程中发现新机房同样接口的系统响应时间变的异常缓慢(普通有100-200ms的时延)。整个迁移过程中代码发布包完全拷贝,所以当时就定位为系统原因进行排查。
整个排查过程通过在代码中关键路径切入耗时监控,从而定位具体耗时部分的代码,缩小排查范围。
排查过程
定位的代码逻辑因为保密原因省略了很多跟问题无关的代码,仅列出核心的代码。
- try/catch过程中的核心代码没有任何耗时的逻辑。
- finally的处理过程中增加了getLocalIP()方法获取本地ip地址。
- 经过添加日志监控耗时怀疑getLocalIP()方法比较耗时。
public RecomResult recommend(RequestId reqId, RecomParam condition) {
lock.readLock().lock();
long timeStart = System.currentTimeMillis();
try {
// 省略核心代码
return result;
} catch (Throwable t) {
// 省略核心代码
} finally {
mapTag.put("ip", engineContext.getLocalIP());
lock.readLock().unlock();
}
}
getLocalIP()方法的实现逻辑就是调用java api获取本机ip地址
- 网上查询类似文章InetAddress.getLocalHost() 执行很慢开始怀疑DNS解析问题。
- 通过for循环执行dig命令确认dns解析时好时坏耗时在100-200ms不等。
- 验证猜测确认是DNS解析问题,并开始通过本地hosts或缓存本地ip地址减少解析。。
public String getLocalIP() {
try {
if (null == localIp) {
localIp = InetAddress.getLocalHost().getHostAddress();
}
} catch (UnknownHostException e1) {
LOG.error("get ip failed, e is {}", e1);
}
return localIp;
}
解决方案
解决方案本身分为代码本身优化和DNS解析优化。
- DNS层优化主要是服务器DNS解析增加本地缓存,牺牲DNS解析的时效性。
- 代码层面优化主要获取本地ip地址后缓存只需要进行一次host解析即可。