ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Haraka STATUS 插件实战指南:用 SMTP 命令实时洞察出站队列与连接池状态

Haraka STATUS 插件实战指南:用 SMTP 命令实时洞察出站队列与连接池状态 后端网络/通信【免费下载链接】HarakaA fast, highly extensible, and event driven SMTP server项目地址https://gitcode.com/gh_mirrors/ha/Haraka点击查看免费下载导读本文围绕 Haraka一个快速、高扩展、事件驱动的开源 SMTP 服务器内置的status插件展开。该插件允许运维人员在**本地回环localhost**发起STATUS命令实时获取出站连接池pool与投递队列queue的内部状态甚至可以直接对队列文件执行“丢弃DISCARD”或“立即重投PUSH”操作。读完本文你将掌握全部 5 条 STATUS 子命令的用法、返回的 JSON 字段含义、集群cluster模式下的合并语义以及对应源码实现与测试用例能够立即上手对生产环境中的 Haraka 实例做状态巡检与队列干预。1. 插件定位本机运维的“后门”status插件由 plugins/status.js 实现其设计目标非常明确只对来自本机localhost的连接开放让管理员无需外部工具即可通过标准 SMTP 会话读取 Haraka 内部运行状态。这一点在源码中体现得淋漓尽致在hook_capabilities钩子中仅当connection.remote.is_local为真时才向会话通告STATUS扩展能力plugins/status.js在hook_unrecognized_command钩子中任何不以STATUS开头的命令都直接放行而远程发来的STATUS命令会被拒绝返回DENY拒绝理由是STATUS not allowed remotelyplugins/status.js。关于“本机”的判定Haraka 会在连接方 IP 写入remote.ip时自动调用net_utils.is_local_ip()计算remote.is_local127.0.0.1 / ::1 等回环地址自然命中见 connection.js。因此启用该插件前请确认两个前提该插件在 config/plugins 中未被注释默认配置里# status是注释状态需手动启用你的监控与运维手段均从本机发起如本机 cron、本机脚本、nc localhost 25等。对应的访问控制测试用例见 test/plugins/status.js当remote.is_local false时STATUS POOL LIST必须返回DENY。2. 通信协议一行命令一段 JSONstatus插件复用了 SMTP 会话本身通过未识别命令钩子hook_unrecognized_command拦截STATUS开头的一行文本交互格式如下请求STATUS CMD [param1] [param2]....响应SMTP code 211 或 500空格JSON 编码响应\r\n命令成功时服务器返回211system status 类别的 SMTP 应答码其后紧跟 JSON 字符串命令无法识别时返回500。文档给出的完整会话示例 220 example.com ESMTP Haraka ready STATUS QUEUE INSPECT 211 {delivery_queue:[],temp_fail_queue:[]}从源码看响应体由connection.respond(211, result ? JSON.stringify(result) : null, () next(OK))发出plugins/status.js当插件内部执行出错如未知命令时则通过next(DENY, err.message)回以 SMTP 拒绝应答。命令解析入口为command_action按空格切分参数第一段必须是POOL或QUEUE否则回调unknown STATUS commandplugins/status.js。实战提示由于响应是 JSON建议用 Python、jq 或 Node 脚本解析例如printf STATUS QUEUE STATS\r\nQUIT\r\n | nc localhost 25即可在一条命令里拿到队列统计。3. 可用命令清单下表汇总了status插件支持的全部子命令官方文档原表逐条展开命令作用返回示例STATUS POOL LIST以host:port为键返回当前活跃的出站连接池映射{mx.example.com:25:{inUse:0,size:3}}STATUS QUEUE STATS队列统计格式为in_progress/delivery_queue 长度/temp_fail_queue 长度1/2/0STATUS QUEUE LIST列出磁盘上的队列文件附带 uuid、domain、mail_from、rcpt_to 等属性[{file:...,uuid:...,domain:...,from:...,to:[...]}]STATUS QUEUE INSPECT合并返回outbound.delivery_queue与outbound.temp_fail_queue的全部内容{delivery_queue:[{id:...}],temp_fail_queue:[{id:...,fire_time:123}]}STATUS QUEUE DISCARD file停止投递指定的队列邮件文件OKSTATUS QUEUE PUSH file立即尝试重新投递指定邮件OK命令解析流程POOL/QUEUE两级 switch与完整命令分支均可在 plugins/status.js 中核对。4. 子命令逐条深度解析4.1STATUS POOL LIST连接池快照作用返回以host:port为键的活跃出站连接池映射。实现位于pool_listplugins/status.jsconst result {} if (server.notes.pool) { for (const name of Object.keys(server.notes.pool)) { const instance server.notes.pool[name] result[name] { inUse: instance.inUseObjectsCount(), size: instance.getPoolSize(), } } }每个池条目包含两个指标inUse当前被占用的连接数正在投递邮件、从池中借出的 socketsize该池的总容量池大小由 outbound 配置决定。这两个指标直接来自连接池对象server.notes.pool中按host:port命名的池实例。当 Haraka 尚未建立任何出站连接时返回空对象{}。使用场景排查“某目标域投递拥塞”时对比inUse与size若inUse长期等于size说明该目标的并发连接已打满需要结合 outbound.ini 中的pool_size等参数调整。4.2STATUS QUEUE STATS三段式队列统计作用返回形如in_progress/delivery_queue/temp_fail_queue的字符串。实现直接调用outbound.get_stats()plugins/status.js其底层逻辑位于 outbound/queue.jslet in_progress 0 const delivery_queue new Queue(async (hmail) { /* 实际投递in_progress/-- */ }) const temp_fail_queue new TimerQueue(1000, { logger }) exports.get_stats () ${in_progress}/${exports.delivery_queue.length()}/${exports.temp_fail_queue.length()}三个数字的含义in_progress正在投递中的邮件数进入delivery_queue工作函数并尚未回调完成的 hmail 数delivery_queue 长度等待投递排队中 运行中的邮件数即 outbound/queue.js 中tasks.length runningtemp_fail_queue 长度临时失败、等待重试的邮件数底层是TimerQueue定时器队列默认以 1 秒为刻度调度重试。使用场景快速判断服务器“当前活跃投递量”与“重试积压量”。配合 outbound.ini 的concurrency_max、temp_fail_period等参数可评估是否需要扩容或调优重试节奏。测试用例验证了返回格式必须匹配正则^\d\/\d\/\d$test/plugins/status.js。4.3STATUS QUEUE LIST磁盘队列文件清单作用列出磁盘上queue_dir目录的全部队列文件及其信封元数据。实现位于queue_listplugins/status.js底层调用outbound.list_queue()→ outbound/queue.js 的_load_cur_queue逐个读取队列文件头部_list_file见 outbound/queue.js。每个条目包含字段含义file队列文件名如1507509981169_..._harakauuid邮件唯一标识来自事务 UUIDTODO 对象属性queue_time入队时间戳毫秒domain目标投递域from信封发件人mail_from.toString()to信封收件人数组rcpt_to.map(r r.toString())关于文件名格式队列文件遵循$arrival_$nextattempt_$attempts_$pid_$uniquetag_$counter_$host的结构解析规则见 outbound/qfile.js其中next_attempt下次尝试时间与attempts失败次数正是重试调度的依据。TODO 头部的字段定义见 outbound/todo.js。使用场景结合 test/queue 目录下的真实队列文件如1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed即可直观对照字段格式。注意QUEUE LIST读取的是磁盘语义与下面的实时QUEUE INSPECT不同。4.4STATUS QUEUE INSPECT实时队列内容作用返回内存中delivery_queue与temp_fail_queue的合并内容。实现位于queue_inspectplugins/status.jscb(null, { delivery_queue: delivery_queue_items.map((hmail) ({ id: hmail.file })), temp_fail_queue: fail_queue_items.map((tqtimer) ({ id: tqtimer.id, fire_time: tqtimer.fire_time, })), })字段含义delivery_queue正在/即将投递的邮件数组每项含id即队列文件名temp_fail_queue等待重试的邮件数组每项含id文件名与fire_time计划触发投递的时间戳。测试用例test/plugins/status.js先向temp_fail_queue添加两条定时任务再断言 INSPECT 返回delivery_queue长度为 0、temp_fail_queue长度为 2。与QUEUE LIST的区别INSPECT 反映的是进程内存中的实时状态仅包含正在处理或等待重试的邮件LIST 反映的是磁盘上的全部队列文件如果已投递成功的文件尚未被清理LIST 仍会将其列出。4.5STATUS QUEUE DISCARD file丢弃待投递邮件作用停止投递指定队列文件。实现位于queue_discardplugins/status.js执行两步操作调用outbound.temp_fail_queue.discard(file)将该文件从重试队列中移除未命中时静默忽略错误fs.unlink(path.join(this.queue_dir || , file))删除磁盘上的队列文件。queue_dir在插件注册时取自outbound/queue模块plugins/status.js其解析规则为优先config.get(queue_dir)其次$HARAKA/queue最后回退到test/test-queueoutbound/queue.js。使用场景管理员判定某封邮件投递无意义如地址永久失效时直接丢弃避免反复重试。测试用例验证 DISCARD 后 INSPECT 中的temp_fail_queue长度从 2 变为 1且被丢弃任务的回调不再触发test/plugins/status.js。4.6STATUS QUEUE PUSH file立即重投作用尝试立即重新投递指定邮件。实现位于queue_pushplugins/status.js在temp_fail_queue.queue中按id查找该文件找到后将其从队列中摘除并立即执行其回调item.cb()该回调会把邮件重新送入投递流程随后响应OK。使用场景当目标域故障恢复后管理员希望跳过剩余退避时间、立刻重试重要邮件时使用。测试用例验证添加一个 1500ms 后触发的定时任务立即 PUSH 后其回调马上执行test/plugins/status.js。注意PUSH 与 DISCARD 均要求参数为磁盘队列文件名即 INSPECT / LIST 返回的id/file。如果文件只存在于内存队列而磁盘文件已被清理PUSH 会找不到对应项但依然返回OK这一点从实现逻辑可以推断使用时需以 INSPECT 结果交叉确认。5. 集群模式下的聚合语义在 cluster多进程模式下各 worker 进程各自持有独立的delivery_queue、temp_fail_queue与连接池status插件通过进程间消息IPC把查询路由到主进程再由主进程广播给所有 worker 并合并结果。路由逻辑见runplugins/status.jsif (server.cluster !/^QUEUE LIST/.test(cmd)) { this.call_master(cmd, cb) // 其余命令经主进程汇总 } else { this.command_action(cmd, cb) // 非集群或 QUEUE LIST 直接本进程执行 }聚合策略由merge_worker_responses实现plugins/status.js与官方文档表述一一对应命令集群聚合方式POOL LIST所有 worker 的池映射合并进一个对象Object.assign({}, ...results)QUEUE STATS各 worker 的N/N/N计数器逐位求和输出单个N/N/N字符串QUEUE INSPECT各 worker 的delivery_queue与temp_fail_queue数组首尾拼接QUEUE LIST始终在主进程执行因它读取所有 worker 共享的磁盘队列文件三条聚合规则的测试覆盖位于 test/plugins/status.js例如[1/2/3, 0/1/0, 2/0/1]求和为3/3/4INSPECT 的delivery_queue/temp_fail_queue数组会跨 worker 顺序拼接。IPC 机制方面主进程通过hook_init_master监听status.request事件并广播call_workers使用Promise.allSettled超时未响应的 worker 被过滤掉worker 通过hook_init_child处理请求并回传结果单次 worker 请求设有1000ms 超时call_worker中的setTimeout见 plugins/status.js。这些细节可从源码结构推断对排查“集群下状态查询偶发缺失”很有价值。非集群模式则无此开销所有命令都在当前进程内直接执行。6. 快速启用与使用示例6.1 启用插件编辑 config/plugins取消status所在行的注释随后重启 Haraka或执行haraka -c /path/to/haraka/config重新加载使配置生效。可用haraka -l查看已安装插件列表、haraka -h status查看插件文档。6.2 交互式使用telnet / nc$ nc localhost 25 220 example.com ESMTP Haraka ready STATUS QUEUE STATS 211 1/2/0 STATUS QUEUE LIST 211 [{file:1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka,uuid:...,queue_time:1507509981169,domain:example.org,from:senderexample.com,to:[rcptexample.org]}] QUIT 221 2.0.0 Bye6.3 脚本化监控# 队列积压监控每分钟一次 echo -e STATUS QUEUE STATS\r\nQUIT\r\n | nc -w 5 localhost 25 | grep ^211 # 丢弃一封确定无意义的待投递邮件 echo -e STATUS QUEUE DISCARD 1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka\r\nQUIT\r\n | nc -w 5 localhost 25 # 目标域恢复后立即重投 echo -e STATUS QUEUE PUSH 1508269674999_1508269674999_0_34002_socVUF_1_haraka\r\nQUIT\r\n | nc -w 5 localhost 25以上队列文件名示例可对照 test/queue 下的真实文件命名验证格式。6.4 运行测试仓库自带完整的插件测试套件可执行npm test -- test/plugins/status.js或运行 run_tests 脚本执行全部测试验证包括访问控制、五个子命令行为、集群合并逻辑在内的所有用例见 test/plugins/status.js。7. 总结与注意事项安全性STATUS仅对本地连接开放remote.is_local判定远程一律DENY如需远程运维应先通过本机隧道或 ACL 限制访问切勿将其直接暴露到公网。数据来源差异POOL LIST、QUEUE STATS、QUEUE INSPECT反映内存实时状态QUEUE LIST反映磁盘文件状态可能包含尚未清理的已投递邮件。集群语义前三者自动聚合所有 worker 结果QUEUE LIST固定由主进程执行。干预类命令DISCARD会删除磁盘队列文件且无法恢复PUSH只对仍在temp_fail_queue中的文件生效请先 INSPECT 确认目标id再操作。响应码成功为 211无法识别的命令按 SMTP 错误应答返回。相关资源插件实现plugins/status.js队列与统计实现outbound/queue.js、outbound/index.js队列文件格式outbound/qfile.js、outbound/todo.js连接池outbound/client_pool.js测试用例test/plugins/status.js插件启用配置config/plugins赞分享后端网络/通信【免费下载链接】HarakaA fast, highly extensible, and event driven SMTP server项目地址https://gitcode.com/gh_mirrors/ha/Haraka点击查看免费下载相关推荐Apache APISIX node-status 插件通过内置接口实时获取 Nginx 连接状态Apache APISIX node status 插件通过内置接口实时获取 Nginx 连接状态 node status 是 Apache APISIX 内API网关后端云原生微服务文档模板复制APIONLYOFFICE Docs创建模板的终极指南文档模板复制APIONLYOFFICE Docs创建模板的终极指南 ONLYOFFICE Docs是一款功能强大的开源在线办公套件提供文档、电子表格、演示文agents24 仓库 Agent Teams 插件实战:详解 /team-status 命令的团队成员、任务状态与进度监控agents24 仓库 Agent Teams 插件实战:详解 /team status 命令的团队成员、任务状态与进度监控 在 Agent Teams 插件AI 插件AI 技能开发工具上一篇ngx-admin 菜单状态管理NgRx 与本地存储同步下一篇SDRPlusPlus 接收 GSM-R 信号实战教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表