DNS调度与HTTP长连接


这篇是补的(升级插件失败后回滚快照,尴尬,上周写完这篇没有做快照,哎,备份的重要性)
业务上有一个访问微软接口(api.cognitive.microsofttranslator.com)的需求,这个域名在海外。

从国内访问的话网络稳定性很差,目前是通过在香港代理机器上开启squid来加速访问的。这几天出现了服务访问api.cognitive延迟升高的情况,把排查过程记录一下。
DNS调度
api.cognitive的域名 CNAME dev-c.microsofttranslator.search.prod.ms.akadns.net. ,有很多的接入IP,见下图:

从香港机器发现域名会被调度到日本、美国、新加坡、孟买等IP(微软销售说他们的IP都是anycastIP,这个实际看上去还真不是,后面再说),调度到孟买、美国IP访问延迟就特别高。在海外新加坡机房机器去请求api.cognitive的域名发现解析到的都是新加坡接入IP(指定8.8.8.8或者1.1.1.1公共DNS都是一样的结果)。
我们的香港代理机器如果把api.cognitive的域名都绑定到新加坡接入IP,存在两个问题,一是api.cognitive的域名接入IP是动态变化的,写死hosts就无法实现自动更新;二是把api.cognitive的域名都指到新加坡,服务这边访问api.cognitive接口延迟不稳定。根据实际情况,最好的办法就是香港代理A机器访问api.cognitive走新加坡接入IP,B机器访问api.cognitive走日本或者其他接入IP,这种分配在实践中网络稳定性最好,下面就是想想如何实现。
我们在新加坡有机器,可以通过部署dnsmasq实现在香港A机器请求新加坡机器的dnsmasq获得最新的api.cognitive IP,写到hosts,squid动态更新。在A机器部署脚本
#!/bin/bash
# 日志文件路径
LOG_FILE="/disk1/check_scripts/update_hosts.log"
# 记录日志的函数
log_message() {
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo "[$timestamp] $1" >> "$LOG_FILE"
}
# 获取 Microsoft Translator API 的 DNS 解析结果
DNS_SERVER="新加坡机器IP"
API_DOMAIN="api.cognitive.microsofttranslator.com"
HOSTS_FILE="/etc/youdao_hosts"
# 使用 dig 命令获取 DNS 解析结果
DNS_RESULT=$(dig @"$DNS_SERVER" "$API_DOMAIN" +short | awk '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {print}')
if [[ -z "$DNS_RESULT" ]]; then
log_message "ERROR: Failed to resolve DNS for $API_DOMAIN using server $DNS_SERVER"
exit 1
fi
# 从 hosts 文件中获取当前配置的 IP 地址
CURRENT_HOST_ENTRY=$(grep "$API_DOMAIN" "$HOSTS_FILE" | cut -d " " -f 1)
if [[ $? -ne 0 ]]; then
log_message "WARNING: No existing entry found for $API_DOMAIN in $HOSTS_FILE"
CURRENT_HOST_ENTRY=""
fi
# 比较 DNS 解析结果与 hosts 文件中的记录
if [[ "$DNS_RESULT" == "$CURRENT_HOST_ENTRY" ]]; then
log_message "INFO: $API_DOMAIN DNS result ($DNS_RESULT) matches current hosts entry. No changes needed."
else
log_message "INFO: $API_DOMAIN DNS result ($DNS_RESULT) does not match current hosts entry ($CURRENT_HOST_ENTRY). Updating hosts file."
# 删除旧的 hosts 条目(如果存在)
if [[ -n "$CURRENT_HOST_ENTRY" ]]; then
sed -i "/^$CURRENT_HOST_ENTRY[[:space:]]\+$API_DOMAIN/d" "$HOSTS_FILE"
if [[ $? -ne 0 ]]; then
log_message "ERROR: Failed to remove old entry from $HOSTS_FILE"
exit 1
fi
fi
# 添加新的 hosts 条目
echo "$DNS_RESULT $API_DOMAIN" >> /etc/hosts
echo "$DNS_RESULT $API_DOMAIN" >> "$HOSTS_FILE"
if [[ $? -ne 0 ]]; then
log_message "ERROR: Failed to add new entry to $HOSTS_FILE"
exit 1
fi
log_message "INFO: Successfully updated $HOSTS_FILE with new DNS result ($DNS_RESULT)"
fi
加到计划任务中5分钟运行一次,检测结果如下:

后面发现这种方案还有一些网络波动,在A和B机器加了一个统计api.cognitive 连接情况的脚本(开启日志logrotate)
#!/bin/bash
# 定义日志路径
LOG_FILE="/var/log/squid_translator_check.log"
# 定义Squid端口
SQUID_PORT=3128
# 获取当前时间戳
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
# 尝试获取目标IP地址(增加错误处理)
RESULT_IP=$(/usr/bin/squidclient -p $SQUID_PORT mgr:fqdncache 2>/dev/null | grep "api.cognitive.microsofttranslator.com" | awk '{print $1}' | uniq)
# 检查是否成功获取IP
if [[ -z "$RESULT_IP" ]]; then
echo "[$TIMESTAMP] ERROR: Failed to get IP for api.cognitive.microsofttranslator.com" >> "$LOG_FILE"
exit 1
fi
# 统计网络连接数(优化命令)
RESULT_NET=$(/bin/netstat -ant | grep "$RESULT_IP" | grep -c "ESTABLISHED")
# 记录结果到日志文件
echo "[$TIMESTAMP] IP: $RESULT_IP, ESTABLISHED Connections: $RESULT_NET" >> "$LOG_FILE"
exit 0
cat /etc/logrotate.d/squid_translator_check
/var/log/squid_translator_check.log {
daily
rotate 7
missingok
compress
delaycompress
notifempty
}

出问题的时候能看到A和B机器连接api.cognitive 的统计方便排查问题。
HTTP长连接
在squid上开启统计访问响应耗时,发现请求api.cognitive的时间都特别长,


我自己用curl测试挺快的,请求几百ms,然后在squid上日志里面也显示几百ms,与业务侧的几十秒差距很大,见下图:


怀疑是业务侧复用了HTTP,咨询了一下开发同学,使用了OkHttp(OkHttp 默认启用 HTTP/1.1 的 keep-alive,会在响应头中协商 Connection: keep-alive,复用TCP连接),用户传过来的都是很长的文本,会截取分词后再分批发送,在squid中看到的就是一条记录。


把之前的检测脚本改成Python实现,可以使用(requests Session)实现HTTP连接复用


使用Python改写检测脚本,使用长文本进行检测,这下看起来对了。



写在最后

3.15号去的时候还有点冷,🦢天鹅一家悠哉悠哉在湖里游玩,结尾用deepseek生成一段,散了。
HTTP长连接不仅是性能优化手段,更是构建高并发、低延迟、高可用系统的基石。通过合理的连接池配置(如示例中的OkHttp客户端)与最佳实践,开发者能够显著提升API调用效率、降低服务器负载。在微服务架构与云原生时代,掌握这一技术细节将成为你应对性能瓶颈的关键武器。