Prometheus 本身不能原生采集 WebSocket 连接数和流量,必须由服务主动暴露 /metrics 端点供其抓取;blackbox_exporter 因协议不匹配无法建立 WebSocket 连接,仅发 HTTP 请求会导致 400 或超时,故不可用于连接数监控。

直接结论:Prometheus 本身不能原生采集 WebSocket 连接数和流量,必须由 WebSocket 服务主动暴露 /metrics 端点,再通过 Prometheus 抓取。硬配 target 去“扫描”或“探测”连接数是无效的。
为什么不能用 blackbox_exporter 或 http_probe 监控 WebSocket 连接数
blackbox_exporter 的 http 模块只能发 HTTP 请求、收 HTTP 响应,它无法建立并维持 WebSocket 连接,更无法在连接生命周期内统计活跃连接数或消息吞吐量。试图用它去“ping” ws:// 地址只会返回 400 Bad Request 或直接超时——这不是服务问题,是协议不匹配。
常见错误现象:
- Prometheus 配置了
static_configs: [{targets: ["localhost:8765"]}],但指标里始终没有websocket_connections_total - 日志中看到
server returned HTTP status 400 Bad Request,误以为是服务没启 - 用 curl 测试
curl -i http://localhost:8765/metrics返回 404,却没意识到服务根本没实现该路由
服务端必须暴露 /metrics:以 Python websockets 为例
Python 的 websockets 库不内置指标导出,需手动集成 prometheus_client 并注册自定义指标。关键不是“连上 WebSocket”,而是“在服务内部维护状态并输出为 Prometheus 文本格式”。
实操要点:
- 定义一个全局计数器:
connections_active = Counter("websocket_connections_active", "Current active WebSocket connections") - 在
echohandler 的入口处调用connections_active.inc(),出口(包括异常退出)调用connections_active.dec() - 新增一个同步 HTTP 路由(如
/metrics),用generate_latest(REGISTRY)返回指标文本 - 确保该路由与 WebSocket 服务共用同一个事件循环或进程,否则计数器不同步
示例片段(非完整服务):
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
from prometheus_client import Counter, generate_latest, REGISTRY
from websockets.server import serve
import asyncio
<p>connections_active = Counter("websocket_connections_active", "Current active WebSocket connections")</p><p>async def echo(websocket, path):
connections_active.inc()
try:
async for message in websocket:
await websocket.send(f"echo: {message}")
finally:
connections_active.dec()</p><h1>单独起一个 HTTP server 暴露指标(注意:不能用 asyncio.run() 重复启 loop)</h1><p>async def metrics_server():
from aiohttp import web
app = web.Application()
app.router.add_get("/metrics", lambda r: web.Response(text=generate_latest(REGISTRY).decode()))
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "0.0.0.0", 9189)
await site.start()</p><h1>启动两个协程:WebSocket + Metrics</h1><p>async def main():
await asyncio.gather(
serve(echo, "0.0.0.0", 8765),
metrics_server()
)</p><p>asyncio.run(main())Prometheus 配置抓取时的关键细节
配置项看似简单,但漏掉任一环节都会导致抓不到数据:
-
job_name名称要语义清晰,比如websocket-api,避免用generic或default -
static_configs中 target 必须指向metrics端口(上例是9189),不是 WebSocket 端口(8765) - 若服务部署在 Kubernetes,Service 的
port和targetPort必须对应到9189,且 Pod 就绪探针不应依赖 WebSocket 端口 - 务必加
honor_labels: true,否则 Prometheus 可能覆盖你代码里设置的 label(如instance="ws-prod-1")
典型 prometheus.yml 片段:
scrape_configs:
- job_name: 'websocket-api'
honor_labels: true
static_configs:
- targets: ['ws-service:9189']Grafana 展示连接数和流量时的易错点
连接数是瞬时值,流量(如消息数)是累计值,二者聚合方式完全不同:
- 查当前连接数用
websocket_connections_active,直接画图即可;不要加rate(),那是给计数器用的 - 查每秒入站消息量,得先有类似
websocket_messages_received_total的 counter,再用rate(websocket_messages_received_total[1m]) - 若指标名带
_total后缀却用了avg_over_time(),结果会越来越小——这是把累计值当瞬时值处理了 - 多个 Pod 实例时,
sum by (job)可汇总总连接数,但别漏掉without (instance),否则同一 job 下不同实例会被拆成多条线
最常被忽略的一点:WebSocket 连接生命周期短(如聊天室用户频繁进出)时,connections_active 波动剧烈,建议在 Grafana 中开启「Min/Max」显示模式,而不是只看平均值——平均值可能掩盖峰值过载风险。

















