如何基于 Grafana Live 推送 WebSocket 更新?
解读
在国内互联网与政企面试中,“CouchDB + 实时可视化” 是高频追问场景:面试官想确认候选人能否把 CouchDB 的增量变更流(_changes)通过安全、低延迟的通道推送到 Grafana Live,并兼顾国产信创环境(如鲲鹏、麒麟)下的网络合规、证书加密与断网续传。问题表面问 WebSocket,实则考察端到端实时数据链路设计与国产化交付经验。
知识点
- CouchDB
_changes参数:feed=continuous、heartbeat、since=now、include_docs、filter=_doc_ids - SM 国密 TLS 1.3 双向证书:在 Nginx 层使用国密套件(
ECC-SM2-WITH-SM4-SM3)做 WSS 卸载,满足等保 3 级 - Grafana Live 通道规范:
scope: datasource、namespace: couchdb.<db>、path: <channel>,需返回 JSON 格式{ values: [...] } - 断点续传机制:利用 CouchDB 的
last_seq做本地 LevelDB 缓存,进程重启后从last_seq重放,防止重复推送 - 背压控制:使用 Node.js
highWaterMark=16对象模式流,结合 Grafana Live 的StreamingResponseData::isBackpressure()做反压,避免内存暴涨 - 国产中间件适配:在银河麒麟 V10 上,用华为 openEuler 提供的 libuv 补丁编译 Node.js 18,解决 ARM 下
uv__kqueue事件空转 Bug
答案
- 在 CouchDB 侧创建专用只读用户
grafana_ro,绑定_reader角色,仅允许访问目标库; - 部署国密 Nginx 网关,监听
wss://couch-live.company.cn:9443,证书采用北京 CA 签发的 SM2 双证,强制 TLS1.3,关闭重协商; - 编写 Node.js 中间层(TypeScript + ES2022),核心流程:
a. 启动时读取本地/opt/couch-grafana/seq.json获取last_seq;
b. 发起GET /db/_changes?feed=continuous&heartbeat=10000&include_docs=true&since=<last_seq>,使用request库设置agent: httpsAgent({ca: [gmca.pem]});
c. 每收到一行 JSON,立即解析doc,按 Grafana Live 要求的格式封装:
d. 通过{ "channel": "couchdb.db1.sensor", "data": { "values": [[doc.timestamp, doc.value, doc.deviceId]] } }grafana/live/publishHTTP 内网接口(POST /api/live/push/couchdb.db1.sensor)推送,Header 带 Authorization: Bearer <Grafana service token>;
e. 成功推送后,异步写seq.json,保证宕机不丢序; - 进程守护采用麒麟系统自带的 systemd(
Restart=always,RestartSec=3s),日志走journald,方便接入中科方德日志审计平台; - 压测验证:用
k6-ws模拟 5k 并发,端到端延迟 P99 < 120 ms,CPU 占用 < 1 核,内存 < 300 MB,满足国内**“双态运维”** 白名单要求。
拓展思考
若面试官追问“政务内网完全物理隔离、无 Grafana 官方镜像”,可继续回答:
- 采用离线 Harbor 导入 Grafana 9.5.3-arm64 镜像,手动打上麒麟 OS 的 seccomp 补丁;
- 将中间层改写为Go 1.20 + Gorilla WebSocket,静态编译后仅 22 MB,零依赖拷贝到隔离环境;
- 用CouchDB 2.3.1 国产版(东方通定制版,内置 SM4 加密存储),通过
_changes?filter=grafana/filter.js在服务端过滤敏感字段,减少 70% 出网流量; - 最终交付包为麒麟 rpm 单文件,
rpm -ivh couch-grafana-bridge-1.0.0-1.ky10.aarch64.rpm一键安装,符合党政机关软件采购“开箱验货” 流程。