知识卡片
Agent多线程分工:用中间队列彻底解耦各线程,谁都不需要关心整体结构
内容
QConf客户端的Agent内部按职责拆成了六个线程,各自只做一件明确的事,彼此之间不直接调用,全部通过中间数据结构(队列)通信:Send线程负责处理ZooKeeper网络数据包的协议解析与封装,把ZooKeeper的事件放入WaitingEvent队列;Event线程依次从WaitingEvent队列取事件处理(关注节点删除、节点值修改、子节点变化、会话过期等事件类型),处理完后把需要更新的Key放入WaitingWriting队列交给下一环节;Msq线程负责从消息队列里获取业务进程发来的”找不到某配置”的请求,同样通过WaitingWriting队列转交出去;Scan线程定期扫描共享内存里的所有配置,发现和ZooKeeper不一致的情况就把对应Key加入WaitingWriting队列(在ZooKeeper重连或轮询周期到达时触发);Main线程是共享内存唯一的写入线程,从ZooKeeper取到数据后统一写入共享内存;Trigger线程负责调用一些周边逻辑(如dump落盘、向业务方发送更新反馈、触发用户自定义脚本)。这套设计的价值在于,四个不同来源的”需要更新共享内存”的触发点(用户请求缺失的Key、ZooKeeper主动通知的变更、定期扫描发现的不一致、异常恢复后的重新拉取)全部被统一汇聚到同一个WaitingWriting队列,再由唯一的Main线程串行消费——每个上游线程完全不需要知道其他线程的存在、也不需要关心自己触发的这次更新最终是怎么被处理的,只需要把”这个Key需要更新”这个事实丢进队列就完成了自己的职责。这正是”各自只负责自己的本职工作,不感知整体结构”这条设计原则的具体体现:用中间队列彻底解耦多个独立的触发源和唯一的处理终点,任何一个线程的内部实现细节发生变化,都不会波及到其他线程,因为它们之间唯一的耦合点就是队列这个统一、简单的接口。
结构图:
flowchart LR
A["用户进程请求\n缺失的Key"] -->|"消息队列"| M["Msq线程"]
B["ZooKeeper事件\n(节点变更/会话过期)"] -->|"WaitingEvent队列"| E["Event线程"]
C["Scan线程\n定期扫描发现不一致"]
D["异常恢复\n(重启/网络中断)后重新拉取"]
M --> W["WaitingWriting队列\n(统一汇聚点)"]
E --> W
C --> W
D --> W
W --> Main["Main线程\n(共享内存唯一写入者)"]
Main --> SM["共享内存"]
Send["Send线程\n处理ZK网络包"] --> B
Trigger["Trigger线程\ndump/反馈/用户脚本"]