排查服务器问题时,我们经常会执行一条命令,看看当前有多少 TCP 连接:
ss -Htn state established | wc -l如果只关心某个服务端口,还可以加上筛选条件:
ss -Htn state established '( sport = :40002 )' | wc -l这能回答“现在有多少连接”,但当问题发生在几个小时前,单次查询就不够用了。
例如,机器人服务偶尔出现集中掉线,等我们登录服务器时,连接可能已经恢复。此时更想知道的是:连接数从什么时候开始下降?持续了多久?哪个端口受到影响?恢复后是否回到了原来的水平?
围绕这些需求,我做了一个小工具:tcp-monitor。
它每分钟采集一次 TCP 连接数,将结果保存到文本文件,再通过 HTML 页面展示趋势。支持多个端口分别查看、自定义时间范围,以及按天滚动清理历史数据。
整个工具不需要数据库,也不需要安装第三方 Python 包。
项目的组成很直接:
tcp-monitor/
├── tcp-monitor.sh # 启动、停止、重启与状态查询
├── tcp_monitor.py # 采集、数据保留及 HTTP 服务
├── index.html # 趋势展示页面
├── test_tcp_monitor.py # 采集与历史数据测试
└── test_launcher.sh # 启动脚本测试采集程序使用 Linux 的 ss 获取连接快照,网页使用原生 Canvas 绘制曲线,无需从外部 CDN 加载图表库。具体实现可以查看仓库中的 Python 脚本和 HTML 页面。
部署时,服务器需要具备 Python 3.8 及以上版本、Bash、`ss` 和 flock。下载项目后,就可以通过启动脚本运行:
git clone https://github.com/adminkk/tcp-monitor.git
cd tcp-monitor
bash tcp-monitor.sh start \
--days 30 \
--ports 40002,40000,11000 \
--bind 0.0.0.0 \
--http-port 8765这条命令表示:同时监控三个本机端口的 TCP 已建立连接,每分钟记录一次,保留最近 30 天的数据,并在 8765 端口提供网页。
这里的端口只是示例,部署时替换成实际服务端口即可。网络允许访问后,在浏览器中打开:
http://服务器IP:8765/启动脚本已经处理了后台运行,不需要额外添加 nohup。使用 bash tcp-monitor.sh 执行,也无需先给脚本添加执行权限。
日常管理只需要几条命令:
# 查看运行状态
bash tcp-monitor.sh status
# 停止采集
bash tcp-monitor.sh stop
# 调整参数后重新启动
bash tcp-monitor.sh restart \
--days 7 \
--ports 40002,40000 \
--bind 0.0.0.0 \
--http-port 8765
# 查看全部参数
bash tcp-monitor.sh --help需要注意,`restart` 使用本次传入的参数和脚本默认值,不会自动记住上一次启动时的命令行参数。经常使用的配置,可以直接设置在启动脚本顶部。例如,默认监控多个端口:
PORTS="${TCP_MONITOR_PORTS:-40002,40000,11000}"配置后,直接执行 bash tcp-monitor.sh start 即可。完整参数说明见项目的 README。
多端口监控的重点,是能够分别观察每个服务。
假设同时监控机器人通信端口、设备通信端口和其他服务端口,只看它们的合计,可能会掩盖某个端口的变化。因此,页面提供了端口下拉框,可以在“全部端口合计”和单个端口之间切换。
切换后,趋势曲线、区间末次连接数、峰值、均值及记录表都会同步更新。时间范围既可以选择最近 1 小时、6 小时、24 小时、7 天,也可以指定开始和结束时间。页面会每分钟重新读取文本,刷新时保留当前选择。
这让排查过程更具体:先看全部端口是否同时下降,再切换到单个端口,判断问题集中在哪个服务;接着缩小时间范围,对照应用日志查找原因。
采集结果默认写入:
data/tcp_connections.txt文件采用 JSON Lines 格式,每行对应一次采样:
{"timestamp":1793923200,"state":"established","ports":[40000,40002],"count":28,"port_counts":{"40000":10,"40002":18}}其中,`count` 是本次监控范围的连接总数,`port_counts` 保存各个端口的数量。多个端口的数据来自同一次 ss 调用,不需要每个端口分别执行一次采集。
文本格式也方便后续处理:可以直接检查原始记录,或者用其他脚本继续分析。如果服务器不方便开放网页端口,也可以下载这个文本文件,在本地打开 index.html,通过“导入文本”离线查看趋势。
对于长期运行的脚本,数据保留比“能持续写入”同样重要。
--days 30 表示滚动保留最近 30 × 24 小时的数据。程序在启动时和每次采样时都会清理过期记录,同时限制样本数量,避免反复重启不断增加记录。
按每分钟一条计算,30 天最多保留约 43201 条记录。实际文件大小随监控端口数变化,但不会在正常运行中无限累积。写入时采用临时文件加原子替换,减少网页读取到不完整记录的情况。这部分逻辑位于 History 类。
另一个容易被忽略的细节,是采集失败不能记成连接数为 0。
连接数为 0,意味着采集成功,而且确实没有符合条件的连接;如果 ss 超时或者执行失败,只能说明这次没有拿到有效结果。
因此,工具会将失败采样保存为 count: null,页面标记为缺测,并断开曲线。超过 90 秒的采样间隔也会断线,避免用一条连续折线掩盖中间的数据空缺。
监控端口发生变化时,也需要保留这种严谨性。
例如,最初只监控 40002,后来增加了 40000。假设合计数从 20 变成 35,这次变化可能来自统计范围扩大,并不代表原来的服务多了 15 条连接。
tcp-monitor 允许更换端口后继续使用原来的数据目录,但每条记录都会保留它当时的端口集合:
查看单个端口时,历史中未监控该端口的时段显示为缺测。
查看合计时,曲线在端口集合变化处断开,并提示统计范围发生了变化。
明细和悬浮提示显示每次采样实际监控的端口。
因此,增加或减少监听端口不必再拆出一套历史文件,也能看清数据口径的变化。
最后,需要区分“TCP 连接在线”和“业务在线”。
对于机器人长连接服务,日常观察一般使用默认的 established。它统计已经建立的 TCP 连接,但不能直接等同于完成认证、心跳正常且业务可用的机器人数量。
其他状态主要用于专项排查:
这些参数是采样过滤条件,当前页面不会将 all 再拆成不同状态的曲线。更改 state 时,仍需使用独立的数据目录,避免混合不同状态的历史数量。
另外,一分钟一次的快照适合观察趋势,可能漏掉两次采样之间出现又结束的短暂波动。遇到秒级断连、握手异常或报文问题,还需要结合实时命令、应用日志或抓包进一步调查。
内置 HTTP 服务没有登录认证。直接绑定 0.0.0.0 时,应控制访问范围;如果只供自己查看,可以显式绑定 127.0.0.1,再通过 SSH 转发访问:
# 服务器上启动
bash tcp-monitor.sh start \
--days 30 \
--ports 40002,40000 \
--bind 127.0.0.1 \
--http-port 8765
# 自己电脑上建立转发
ssh -N -L 8765:127.0.0.1:8765 用户名@服务器地址随后在本机浏览器打开 http://127.0.0.1:8765/。
当我们需要给几台 Linux 服务器补充 TCP 连接历史时,这样一个小工具就能留下可回看的依据:连接数什么时候变化、涉及哪些端口、持续了多久。带着明确的时间窗口再去查日志,通常比故障恢复后反复执行一次 ss 更有帮助。
项目地址:adminkk/tcp-monitor。
评论区