如何关闭实时监听减少唤醒?
解读
在 CouchDB 的国内落地场景中,实时监听(_changes feed)常被用来做增量同步、大屏推送或微服务解耦。
若监听粒度太细、心跳间隔太短,会频繁唤醒 Erlang 调度器与操作系统线程,导致 CPU 飙高、移动设备电量掉得快、云主机被监控告警。
面试官问“如何关闭实时监听减少唤醒”,本质想确认两点:
- 你能否彻底关闭不必要的监听;
- 若业务仍需“准实时”,你能否降低频率、减少空转、降低长连接占用,而不是粗暴一刀切。
回答时要兼顾离线优先与国内移动网络特点,给出可落地的配置、代码与运维策略。
知识点
- feed 类型:continuous、longpoll、eventsource、normal;前三种会保持 TCP 长连接,是唤醒主因。
- 心跳机制:
heartbeat默认 6000 ms,设为0可关闭心跳,但可能让防火墙或网关提前 RST。 - filter 函数:服务端
_design/doc里的filter可减少下行流量,但不能减少连接唤醒;若过滤逻辑复杂,反而增加 CPU。 - 超时参数:
timeout决定 longpoll 最长挂起时间;国内 4G/5G 基站 NAT 超时普遍 30 s–120 s,设置过长会被网关强断,过短则频繁重连。 - Erlang VM 调度器唤醒:每来一次 TCP 包都会唤醒 scheduler;减少包频率即可减少上下文切换。
- 移动端省电策略:国内 Android 厂商对后台长连接有“对齐唤醒”限制,iOS 更严格,必须降级为轮询+增量 seq 方案。
- 云厂商 SLB 空闲超时:阿里云 CLB、腾讯云 LB 默认 90 s 断开空闲连接;需把
heartbeat调到 30 s 以内或改用正常轮询。 - 集群层面:
_changes读最终会落到主分片节点,若监听 QPS 高,会拖垮整个分片;可通过反向代理限流或关闭监听端口隔离。
答案
分三级策略,按业务容忍度选择:
-
彻底关闭监听(零唤醒)
- 服务端:在
local.ini的[httpd]段加
重启节点,[httpd] enable_changes = false_changes接口直接返回 404,所有 feed 类型不可用。 - 若只想禁止外网访问,不改源码,可在 nginx 层 return 403:
location ~ ^/.*/_changes { return 403; }
- 服务端:在
-
保留接口但降低频率(低唤醒)
- 调用方把
feed=continuous改成feed=normal并加大since=last_seq的轮询周期,例如 30 s 一次; - 若必须用 longpoll,把
timeout=25000&heartbeat=25000,让 NAT 网关 30 s 内收到一次包,既保活又把唤醒降到 40 次/小时; - 移动端用JobScheduler/WorkManager做对齐唤醒,批量请求,一次取 500 条,减少无线射频激活。
- 调用方把
-
集群运维兜底
- 对高并发监听路径加 nginx limit_req,如 10 r/s,超量直接 503,保护 Erlang 调度器;
- 监控
erlang:system_info(scheduler_wall_time),若 scheduler 唤醒占比 > 15%,立即把enable_changes动态关闭(热更新local.ini后执行curl -X POST http://127.0.0.1:5986/_node/_local/_config/httpd/enable_changes -d '"false"'),无需重启节点。
一句话总结:先评估业务能否接受“轮询+增量”,不能则把心跳对齐到国内网关 NAT 超时,最后用 nginx 或配置开关兜底,确保唤醒可控。
拓展思考
- 若业务必须实时,但又要省电,可考虑把 CouchDB 的
_changes换成 MQTT 推送网关,由网关聚合后再用系统级推送通道(APNs、厂商 Push)下发,长连接只在网关层,移动端真正零常驻。 - 国内政务云常要求关基设备关闭一切非必要接口,此时可把
enable_changes做成 Ansible 一键角色,上线即关闭,审计直接 grep 配置即可合规。 - 未来升级到 CouchDB 4.x 的分片级 changes 索引后,可给监听接口加 RUM 索引提示,让调度器只唤醒指定分片,进一步把 CPU 占用降到 3.x 的 1/3,兼顾实时与节能。