psutil可跨平台实时采集CPU和内存负载,需用cpu_percent(interval=1)和virtual_memory().percent获取浮点值,避免getloadavg()或shell命令解析;远程采集宜SSH执行轻量Python命令,结合Consul/etcd服务发现与健康同步,并用组合指标(如0.6×CPU+0.4×MEM)实现稳定加权调度。

用 psutil 实时采集各节点 CPU 和内存负载
集群负载均衡的前提是准确感知每个节点的实时压力,不能依赖 top 或 htop 这类交互式命令——它们输出不稳定、难解析。推荐用 psutil,它跨平台、无 shell 依赖,且能直接拿到浮点型负载值。
关键点:CPU 使用率应调用 psutil.cpu_percent(interval=1),而非 psutil.getloadavg()(后者是系统平均负载,单位和含义都不同);内存使用率要基于 psutil.virtual_memory().percent,别用 free -m 解析——字段位置在不同 Linux 发行版里可能偏移。
- 务必设置
interval=1或更高,否则首次调用恒为 0.0 - 远程采集时,不要在目标机器上运行 Python 脚本再回传结果——延迟高、失败难定位;改用 SSH 执行轻量命令(如
python3 -c "import psutil; print(psutil.cpu_percent(1), psutil.virtual_memory().percent)")更可靠 - 如果节点启用了 cgroups v2 或容器化环境,
psutil默认仍读宿主机指标;需确认是否要进容器 namespace 采集,此时得配合nsenter或改用docker statsAPI
用 requests + Consul 或 etcd 做服务发现与健康状态同步
单纯知道负载数字没用,必须关联到“哪个服务实例跑在哪台机器上”。硬编码 IP 列表会迅速过时,尤其在 Kubernetes 或 Nomad 环境下节点动态伸缩频繁。
Consul 是最轻量的选择:consul kv get -recurse service/worker/ 可拉取所有 worker 实例注册信息,包括 IP、端口、标签(如 "env=prod")、自定义健康检查字段(比如 "load_cpu=68.2")。你不需要自己维护心跳,Consul agent 会自动上报并剔除失联节点。
立即学习“Python免费学习笔记(深入)”;
- 别把负载值直接写进 Consul KV 的 value 字段——KV 不支持原子更新多个字段;建议用 JSON 格式存整条状态,例如
{"cpu": 68.2, "mem": 72.1, "ts": 1717023456} - 避免高频轮询:
consul kv get加?wait=10s参数可长连接等待变更,减少无效请求 - 如果集群已用 etcd,优先走
etcd3Python 客户端的 watch 接口,比 HTTP 轮询更实时;但注意 etcd 的 lease 续约必须由客户端主动维持,漏掉就可能被踢出观察列表
实现加权轮询(WRR)或最小连接数(LeastConn)调度策略
负载均衡不是“把请求塞给当前 CPU 最低的机器”,而是要在稳定性、延迟、资源利用率之间做权衡。简单取 min(CPU, MEM) 容易引发抖动——某节点刚完成一个大任务,CPU 瞬间跌到 5%,但内存还占着 90%,这时继续导流会 OOM。
推荐组合指标:用 0.6 * cpu_percent + 0.4 * mem_percent 得到综合负载分(系数按业务特征调整),再套 LeastConn 思路——选当前活跃连接数最少、且综合负载低于阈值(如 75%)的节点。
- 连接数统计别依赖
netstat(慢且需 root);改用psutil.net_connections()过滤目标端口,再计数,快且权限友好 - WRR 必须动态更新权重:每 30 秒重算一次各节点权重 =
max(1, int(100 / (1 + load_score))),避免权重归零导致节点彻底下线 - 永远保留一个 fallback 节点(比如负载最低的 2 台),当其他节点全部超阈值时,强制走 fallback,而不是报错——这是生产环境底线
处理 SSH 连接失败、目标进程僵死、Consul 网络分区等典型故障
自动化脚本一旦后台常驻,最常见的崩溃点不是算法逻辑,而是网络或权限异常没兜住。比如 paramiko 连不上某台机器,不设 timeout 会卡死整个调度循环;又比如 Consul 返回 500,脚本没判空就直接 json.loads(None) 报错退出。
- 所有外部调用必须包在
try/except里:paramiko.SSHException、requests.exceptions.ConnectionError、json.JSONDecodeError都要单独处理,记录日志后跳过该节点,不中断主流程 - 对关键路径加超时:SSH 执行命令用
timeout=5,HTTP 请求用timeout=(3, 7)(3 秒 connect,7 秒 read) - 本地缓存最近一次成功的节点状态(写入
/tmp/load_cache.json),当所有远程采集全失败时,降级使用缓存数据继续调度——至少比停摆强
真正难的不是算出哪台机器该接新请求,而是让这个判断在磁盘满、网络抖动、Consul leader 切换、Python GIL 锁死的任意组合下都不崩。每多一层防御,上线后半夜告警就少一条。


















