如何避免Procmail导致平台崩溃并实现单进程运行?
解决Procmail批量邮件涌入时多进程崩溃问题
要让Procmail每次仅运行一个进程,核心是通过排他锁限制实例数量,同时调整邮件处理逻辑避免子进程泛滥。以下是具体方案:
1. 修改Procmail配置,添加全局排他锁
通过目录锁(原子操作,可靠性更高)确保同一时间只有一个Procmail实例执行邮件处理流程。修改后的配置如下:
SHELL = /bin/sh LOGFILE = /var/log/procmail.log LOGABSTRACT = "all" VERBOSE = "on" # 创建全局排他锁目录(原子操作,仅第一个进程能成功创建) :0: * ? mkdir /var/lock/procmail_global_lock 2>/dev/null { # 去掉原规则的"c"标志(避免启动子进程并行处理),改为串行执行脚本 :0 | php /srv/platform/laravel/artisan platform:catchemail >> /var/log/procmail_catchemail.log 2>&1 # 投递邮件到日志文件(带锁确保写入安全) :0: /var/log/platform_catchemail } # 清理锁目录(进程正常退出时执行,防止异常锁残留) :0 * ? rmdir /var/lock/procmail_global_lock 2>/dev/null
关键修改说明:
- 目录锁机制:
mkdir是原子操作,后续进程会因目录已存在而等待锁释放,确保单实例运行。 - 移除
c标志:原规则的:0c会创建子进程并行处理邮件,去掉c后改为串行处理,减少进程数量。 - 锁目录清理:
rmdir仅在目录为空时执行,避免异常退出后锁残留导致后续进程无法启动。
2. 额外优化:避免PHP脚本生成子进程
如果platform:catchemail artisan命令本身会生成子进程,需要在脚本内部添加锁机制(比如PHP的flock文件锁),确保同一时间只有一个脚本实例运行:
// 在artisan命令的handle方法开头添加锁逻辑 $lockFile = fopen('/var/lock/catchemail_script.lock', 'w'); if (!flock($lockFile, LOCK_EX | LOCK_NB)) { // 已有实例运行,直接退出 exit(0); } // 原脚本逻辑... // 脚本结束后释放锁 flock($lockFile, LOCK_UN); fclose($lockFile);
3. 进阶方案:队列化处理
如果批量邮件量极大,推荐将Procmail改为仅负责邮件暂存,用单进程定时脚本处理队列:
Procmail配置修改(仅存队列):
SHELL = /bin/sh LOGFILE = /var/log/procmail.log LOGABSTRACT = "all" VERBOSE = "on" # 将邮件暂存到队列目录(带锁确保写入安全) :0: /var/spool/procmail_queue/
定时处理脚本(通过Cron执行):
添加Cron任务,每分钟运行一次单进程脚本处理队列:
* * * * * php /srv/platform/laravel/artisan platform:process_queue >> /var/log/procmail_queue.log 2>&1
这样Procmail仅做快速投递,实际邮件处理由单进程脚本串行完成,彻底避免多进程资源耗尽问题。
内容的提问来源于stack exchange,提问作者alex spencer
相关产品推荐
相关产品推荐

