Margrop
Articles278
Tags441
Categories23
1password AC ACP AI AP API AppDaemon Aqara CI/CD Caddy Cloudflare Cookie 认证 Cron D1 Date Diagrams.net Docker Docker Compose Electerm Gateway GitHub Actions HA HADashboard Hexo HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS Markdown MiniMax Multi-Agent MySQL NAS Nginx Node.js OOM OpenAI OpenClaw OpenResty PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE SOCKS5 SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VPN VPS Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop authy autofs backup baidupan bash bitwarden boot brew browser caddy2 cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit container crontab ctyun ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump dylib edge exception export fail2ban feign firewall-cmd flow frp frpc frps fuckgfw function gcc gfw git github golang gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https idea image img img2kvm import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jni jnilib jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live low-code lvm lxc m3u8 mac macos mariadb markdown maven md5 microcode mirror modem modules monitor mount mstsc mysql n2n n5105 nas network nfs node node-red nodejs nohup notepad++ npm nssm ntp oop openfeign openssl os otp ovz p14 packet capture pat pdf pem perf ping pip plugin png powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reflog remote remote desktop renew repo resize retina root route router rule rules runtime safari sata scipy-notebook scoping scp server slmgr so socks source spk spring springboot springfox ssh ssl stash string supernode svg svn swagger sync synology systemctl systemd tap tap-windows tapwindows telecom template terminal tls token totp tvbox txt ubuntu udisk ui undertow uninstall unlocker upgrade url v2ray vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 云电脑 交换机 人机协作 代理 体检 值班 健康检查 光猫 公网IP 内存 内存优化 内网IP 写作 升级 协作 博客 反向代理 启动 周一 周末 夏令时 多智能体 多节点 多节点管理 天猫精灵 天翼云 安全 安装 定时任务 容器 容器网络 导入 小米 常用软件 广告屏蔽 序列号 应用市场 异常 心跳 感悟 打工 打工人 技术 抓包 排查 描述文件 故障 故障排查 效率 效率工具 无服务器 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 流程 流程图 浏览器 漫游 激活 火绒 焦虑 玄学 生活 电信 画图 监控 直播源 直觉 端口扫描 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 虚拟机 认证 证书 语雀 超时 路由 路由器 软件管家 软路由 运维 运维监控 连接保活 连接问题 通信机制 部署 配置 钉钉 镜像 镜像源 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客

Hitokoto

Archive

记一次服务"表面健康但用户无法连接"的深度排查实战

记一次服务"表面健康但用户无法连接"的深度排查实战

前言

运维工作中最让人头疼的问题,不是服务彻底挂了那种一眼就能看出来的故障,而是那种”表面看起来正常,但用户就是用不了”的隐蔽问题。最近我们就遇到了这样一个案例:Gateway进程在运行,端口在监听,本地telnet也通,但就是有部分请求过不去、连接经常断。监控一切正常,但用户体验很差。本文记录了完整的问题排查和解决过程,供遇到类似问题的同学参考。

问题背景

业务场景

我们的自动化运维系统通过OpenClaw Gateway接入多个消息通道(飞书、企业微信等),为团队提供7x24小时的自动化服务。Gateway作为核心入口,需要保持长连接,任何连接质量问题都会直接影响用户体验。

问题现象

  • 故障时间:近期某周三早上
  • 故障表现
    • 用户反馈连接经常断开
    • 部分API调用超时
    • 监控显示Gateway进程正常运行,端口18789正常监听
    • 从服务器本地telnet测试端口:通
    • 从客户端连接:间歇性失败
  • 影响范围:部分用户受影响,无法正常使用自动化服务
  • 环境信息
    • 操作系统:Ubuntu 24.04
    • 节点:VM151、VM152、VM153
    • 消息通道:企业微信 WebSocket 模式
    • 部署架构:多节点部署,负载均衡

初步分析

根据问题现象,可能的原因有以下几种:

  1. 网络策略问题:防火墙或网络设备阻止了部分连接
  2. 连接数限制:服务器连接数达到上限,新连接被拒绝
  3. 中间件配置问题:代理或负载均衡器配置变更
  4. 客户端配置问题:客户端使用了错误的连接参数
  5. 长连接超时:中间设备(如防火墙、负载均衡器)设置了较短的超时时间

排查过程

第一步:确认服务状态

首先检查Gateway进程是否正常运行:

1
2
3
4
5
6
7
8
9
10
11
# 检查Gateway进程
ps aux | grep openclaw-gateway | grep -v grep

# 检查进程启动时间和运行状态
systemctl status openclaw-gateway

# 检查端口监听状态
ss -tlnp | grep 18789

# 检查进程监听的地址
netstat -tlnp | grep 18789

结果:进程正常运行,端口正常监听,服务状态看起来一切正常。

第二步:测试本地连通性

从服务器本地测试连接:

1
2
3
4
5
# 本地测试
curl -v http://localhost:18789/health

# 或者
telnet localhost 18789

结果:本地连接正常,返回{"ok":true}

第三步:测试远程连通性

从客户端所在机器测试:

1
2
3
4
5
6
7
8
# 测试远程连接
curl -v http://<服务器IP>:18789/health

# 测试端口连通性
telnet <服务器IP> 18789

# 使用nc测试
nc -zv <服务器IP> 18789

结果:部分客户端无法连接,连接建立后很快断开。但也有些客户端可以正常连接。

第四步:检查网络路径

使用traceroute或mtr检查网络路径:

1
2
3
4
5
6
7
8
# 检查路由
traceroute <服务器IP>

# 或者使用mtr(更详细)
mtr <服务器IP>

# 检查是否有丢包
ping -c 100 <服务器IP>

结果:网络路径正常,没有丢包,延迟也在正常范围内。

第五步:检查防火墙规则

检查服务器防火墙和云平台安全组规则:

1
2
3
4
5
6
7
8
# 检查iptables规则
iptables -L -n

# 检查ufw状态
ufw status

# 检查云平台安全组配置(如果适用)
# 需要登录云控制台检查

结果:防火墙规则正常,没有阻止到18789端口的连接。

第六步:检查连接数限制

检查服务器连接数:

1
2
3
4
5
6
7
8
9
10
11
# 检查当前连接数
netstat -an | grep 18789 | wc -l

# 检查连接状态分布
netstat -an | grep 18789 | awk '{print $6}' | sort | uniq -c

# 检查系统级连接限制
cat /proc/sys/net/core/somaxconn
cat /proc/sys/net/ipv4/tcp_max_syn_backlog

# 检查应用级连接限制(根据应用配置)

结果:连接数在正常范围内,没有达到限制。

第七步:检查中间件配置(关键发现)

经过以上排查,问题仍然存在。这时我开始怀疑是中间件的问题。

检查负载均衡器或代理配置:

1
2
3
4
5
6
7
8
9
# 检查nginx/apache配置(如果有)
cat /etc/nginx/nginx.conf
cat /etc/nginx/conf.d/*.conf

# 检查HAProxy配置(如果有)
cat /etc/haproxy/haproxy.cfg

# 检查代理服务配置
cat /etc/proxy/proxy.conf

发现关键问题:在代理配置中,连接超时时间设置较短,导致长连接被意外中断。

第八步:分析长连接中断原因

确认中间件配置后,发现问题根因:

  1. 代理层超时设置:代理服务的空闲超时设置太短(默认60秒)
  2. Keep-Alive配置:HTTP Keep-Alive超时时间未设置或设置过短
  3. 中间件重启:代理服务在凌晨进行了配置重载,导致部分长连接被断开

解决方案

方案对比

方案 优点 缺点 推荐程度
调高代理超时时间 简单,改动小 需要确认合理性 推荐
改用短连接 无需维持长连接 增加延迟,影响体验 不推荐
绕过代理直连 减少中间环节 需要修改客户端配置 可选
增加连接心跳 维持连接活跃 增加复杂度 辅助

实施修复

1. 调整代理超时配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 登录到代理服务器
ssh root@<代理服务器IP>

# 找到代理配置文件
find /etc -name "*.conf" -exec grep -l "timeout" {} \;

# 修改超时配置(以常见代理为例)
# 修改空闲超时时间
sed -i 's/timeout.*=.*[0-9]*/timeout = 3600/g' /etc/proxy/proxy.conf

# 或者修改nginx配置
sed -i 's/proxy_read_timeout [0-9]*s/proxy_read_timeout 3600s/g' /etc/nginx/nginx.conf

# 重启代理服务
systemctl restart proxy
# 或者
nginx -t && nginx -s reload

2. 启用连接心跳(保活)

在Gateway配置中启用WebSocket心跳:

1
2
3
4
5
6
7
8
9
# openclaw配置文件
gateway:
channels:
wecom:
enabled: true
# 心跳间隔(毫秒)
heartbeatInterval: 30000
# 心跳超时(毫秒)
heartbeatTimeout: 120000

3. 配置连接复用

确保客户端和服务器都使用HTTP Keep-Alive:

1
2
3
4
5
# 服务器端nginx配置
proxy_http_version 1.1;
proxy_set_header Connection "";

# 或者在代码中确保使用长连接

验证修复效果

修复后进行验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 测试长连接稳定性
# 在客户端执行
for i in {1..100}; do
curl -s -o /dev/null -w "%{time_total}s\n" \
-H "Connection: keep-alive" \
http://<服务器IP>:18789/health
sleep 1
done

# 2. 观察监控数据
# 查看Gateway连接数趋势
curl http://localhost:18789/metrics | grep connections

# 3. 检查代理服务日志
tail -f /var/log/proxy/access.log | grep -v "200"

# 4. 用户反馈确认
# 联系受影响用户确认是否恢复正常

一键排查脚本

如果你遇到了类似的”服务正常但连接不稳定”问题,可以使用以下脚本进行快速排查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash

# 服务表面正常但连接不稳定 - 快速排查脚本

echo "=== 服务连接问题快速排查 ==="
echo ""

TARGET_HOST="$1"
TARGET_PORT="${2:-18789}"

if [ -z "$TARGET_HOST" ]; then
echo "用法: $0 <目标主机> [端口]"
echo "示例: $0 192.168.1.100 18789"
exit 1
fi

# 1. 检查Gateway进程
echo "1. 检查Gateway进程:"
ps aux | grep openclaw-gateway | grep -v grep | head -3
echo ""

# 2. 检查端口监听
echo "2. 检查端口监听状态:"
ss -tlnp | grep ":${TARGET_PORT} "
echo ""

# 3. 测试本地连接
echo "3. 测试本地连接:"
curl -s -m 5 http://localhost:${TARGET_PORT}/health || echo "本地连接失败"
echo ""

# 4. 测试远程连接(多次)
echo "4. 测试远程连接(5次):"
for i in {1..5}; do
result=$(curl -s -m 5 -w "%{http_code}" -o /dev/null http://${TARGET_HOST}:${TARGET_PORT}/health 2>&1)
echo " 第${i}次: ${result}"
sleep 2
done
echo ""

# 5. 检查连接数
echo "5. 检查当前连接数:"
netstat -an | grep ":${TARGET_PORT} " | wc -l
echo ""

# 6. 检查网络路径
echo "6. 检查网络延迟:"
ping -c 5 ${TARGET_HOST} | tail -1
echo ""

# 7. 检查代理配置(如果可访问)
echo "7. 检查代理配置(如果有):"
if [ -f /etc/proxy/proxy.conf ]; then
grep -E "timeout|keepalive" /etc/proxy/proxy.conf
elif [ -f /etc/nginx/nginx.conf ]; then
grep -E "timeout|keepalive" /etc/nginx/nginx.conf
else
echo " 未找到代理配置文件"
fi

echo ""
echo "=== 排查完成 ==="

常见问题解答

Q1:为什么本地连接正常,远程连接失败?

A:这通常是网络层面问题,可能原因包括:1)防火墙只限制了远程访问,本地访问放行;2)网络设备(如负载均衡器)配置问题;3)客户端到服务器之间有中间代理,代理配置问题;4)NAT或端口映射问题。建议按本文的排查步骤逐一排查。

Q2:如何判断是不是中间件问题?

A:可以通过绕过中间件直接连接来测试。如果直连正常但经过代理就不行,那基本可以确定是中间件问题。另外,检查中间件的日志也能发现蛛丝马迹。

Q3:长连接和短连接该怎么选择?

A:对于需要实时交互的场景(如消息通道),推荐使用长连接。对于低频请求场景,短连接更简单。选择取决于业务场景需求,不能一概而论。

Q4:Keep-Alive超时设置多少合适?

A:这取决于你的业务场景。一般建议:1)实时消息场景:设置较长(3600秒以上);2)普通API场景:中等(300秒左右);3)低频场景:短连接即可。需要平衡连接稳定性和服务器资源占用。

Q5:心跳间隔设置多少合适?

A:心跳间隔应该小于中间件的超时时间,一般设置为超时时间的1/3到1/2。例如,如果代理超时60秒,心跳间隔建议设置在20-30秒左右。

根因分析与预防措施

根因分析

这次问题的根本原因是:中间件配置变更未同步到所有相关方

在本次案例中,代理服务在凌晨进行了配置重载,虽然reload了服务,但由于某些配置项需要完全重启才能生效,导致部分长连接超时配置没有正确应用。这是一个典型的配置变更管理问题。

预防措施

  1. 配置版本控制:所有配置变更都应该纳入版本控制,变更前review,变更后记录

  2. 配置同步机制:建立配置同步机制,确保所有节点配置一致

  3. 变更通知流程:任何配置变更都需要通知到所有相关方,包括:配置变更内容、影响范围、生效时间

  4. 监控告警增强

    • 监控代理服务的配置版本
    • 监控连接超时次数
    • 监控长连接稳定性
  5. 定期巡检:定期检查所有节点配置一致性,提前发现问题

  6. 灰度发布:配置变更采用灰度方式,先在一台机器上验证,确认无误后再全量发布

经验总结

  1. 服务正常不等于连接正常:进程在跑、端口在监听不代表用户体验没问题,需要实际测试连接

  2. 本地正常不代表远程正常:测试连接时一定要从客户端角度测试,不能只看服务器本地

  3. 中间件是连接问题的重灾区:很多连接问题实际上不是服务端问题,而是中间件(代理、负载均衡器)配置问题

  4. 配置变更要管理:配置变更管理是运维工作的重要一环,变更不规范,问题不断

  5. 监控要全面:监控不仅要看服务器层面指标,还要看连接层面指标

  6. 文档要同步:排查过程中发现的问题和解决方案要及时记录,形成知识积累

结语

“服务表面健康但连接不稳定”是一个隐蔽性很强的问题,常规的进程和端口监控往往发现不了。这类问题需要运维工程师主动从用户角度测试连接,而不是只依赖监控数据。希望本文的排查思路能帮助到遇到类似问题的同学。


作者:小六,一个在上海努力搬砖的程序员

Author:Margrop
Link:http://blog.margrop.com/post/2026-04-22-service-looks-healthy-but-users-cannot-connect/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可