Workerman进程卡在gethostbyname或dns_get_record调用上,因其默认使用阻塞式DNS解析,导致整个Worker停在建连前的DNS阶段,无法处理新请求。

Workerman 进程卡在 gethostbyname 或 dns_get_record 调用上
Workerman 默认使用 PHP 原生的阻塞式 DNS 函数(如 gethostbyname、stream_context_create 中的 resolve_timeout 未设或设得过大),一旦下游服务域名(比如 MySQL、Redis、HTTP 客户端请求的 API 域名)解析慢,整个 Worker 进程就会停在那,无法处理新连接或已建立连接的数据。这不是 Workerman 自身 bug,而是 PHP 运行时行为。
典型表现是:php your_start_file.php status 显示某个 Worker 的 connections 长期不变化、total_request 停滞、mem_usage 缓慢上涨,且 recv_fail 或 send_fail 开始上升——说明它卡在建连前的 DNS 阶段,没机会发包。
- 检查所有外部依赖是否用了域名:MySQL 主机填的是
db.internal而非 IP;Redis 连接地址是cache.company.local:6379;HTTP 客户端调用https://api.v1.company.com - 确认这些域名是否被硬编码在配置文件、Worker 启动逻辑、或连接池初始化代码里(比如
new Redis()构造时传入域名) - 临时把关键依赖改成 IP 地址测试:若改完后
status恢复活跃、connections正常波动,基本可锁定是 DNS 问题
用 strace 或 lsof 直接观察 Worker 进程在等什么
不要只看 Workerman 状态输出,要进系统层确认进程到底卡在哪。对一个疑似卡住的 Worker PID(从 status 输出中拿到 pid 字段)执行:
strace -p <PID> -e trace=connect,sendto,recvfrom,getaddrinfo,gethostbyname -s 256
如果看到反复出现 getaddrinfo("db.internal", ...) 后长时间无返回,或 connect 调用一直失败并重试,就是 DNS 解析阻塞了后续流程。
-
getaddrinfo是现代 PHP(7.4+)默认使用的函数,比gethostbyname更通用,但也更可能因 DNSSEC 或递归超时卡住 - 若
strace显示大量connect(…, AF_INET, …)失败且错误码是EINPROGRESS后无进展,说明 DNS 返回了 IP,但 TCP 层连不上——这不属于 DNS 问题,需另查网络策略或目标端口 - 用
lsof -p <pid> | grep -i "dns\|53"</pid>可快速判断该进程是否正在向 DNS 服务器(如114.114.114.114:53)发 UDP 包且未收响应
Workerman 启动前预热 DNS 缓存,或强制跳过运行时解析
Workerman 不提供内置 DNS 缓存机制,也不能像 Node.js 那样用 dns.lookup + dns.resolve 分离同步/异步。所以最稳妥的方式是在 Worker 初始化阶段就完成解析,并复用结果。
- 在
Worker::onWorkerStart回调里,对所有依赖域名调用一次gethostbyname或dns_get_record,把结果存到全局变量或连接池配置中;后续创建连接时直接用 IP - MySQL 连接可改用
mysqli或PDO的PDO::ATTR_TIMEOUT+mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, 1),但注意:PHP 的 connect timeout **不包含 DNS 解析时间**,它只控制 TCP 握手阶段 - 若必须用域名且无法预热(比如动态路由场景),考虑用
c-ares扩展 + 异步 DNS 封装,但需自行集成,Workerman 不原生支持
容器/K8s 环境下 CoreDNS 或宿主机 DNS 配置失效
Workerman 部署在 Kubernetes 或 Docker 中时,/etc/resolv.conf 往往指向 10.96.0.10(CoreDNS ClusterIP),但若 coredns Pod 异常、NetworkPolicy 拦截了 UDP 53、或 VPC 安全组丢弃了 DNS 流量,所有 Worker 都会集体卡住。
- 进容器执行
nslookup db.internal 10.96.0.10,看是否超时;再试nslookup db.internal 8.8.8.8,对比是否仅内部 DNS 失效 - 检查
kubectl get pods -n kube-system -l k8s-app=kube-dns是否全部 Running;kubectl logs -n kube-system <coredns-pod></coredns-pod>是否有REFUSED或timeout日志 - 确认 Pod 的
dnsPolicy是ClusterFirst(默认),而不是Default或None;后者会导致 DNS 查询直接走宿主机,而宿主机可能没配好
/etc/resolv.conf 或本地缓存可能已变更,而子进程不会自动刷新**。这意味着即使你改了 DNS 配置,也必须重启整个 Workerman 服务,仅 reload 不生效。


















