PHP: 8.1
workerman/webman-framework: v1.6.4
workerman/crontab: v1.0.6
webman/console: v1.3.0
目前项目是通过自定义进程来实现一个进程执行一个crontab(这个crontab的worker会new一个console执行run方法执行业务), 想在自定义进程的worker的onWorkerStop的时候进行一些企微告警
问题: webman自带的monitor监控进程会对所有worker的内存使用量进行监控, 如果发现超过memory_limit的80%的时候会主动的重启自定义进程, 但是自定义进程的代码有可能也是正常执行完毕了的(没有error或者exception), 就会触发我写的onWorkerStop的回调方法发送企微告警, 我希望在onWorkerStop的时候能知道前面的业务代码是否有error或者exception, 如果没有就不发送企微告警, 但是目前只能通过set_error_handler()拿到error, 拿不到异常(exception), 看框架源码应该是Worker在执行run的时候已经捕获了onWorkerStart()时的异常,进行了标准输出和日志记录, 所以set_exception_handler()获取不到任何异常

<?php
namespace process\crontab;
use app\queue\redis\WebHookQiWeiBot;
use Workerman\Crontab\Crontab;
use Workerman\Worker;
class Base
{
const RULE = ''; // 默认规则为空,由子类重写
protected Crontab $crontab;
/**
* @var bool
*/
protected bool $hasSendWorkerStopAlarm = false;
private array $lastError = [];
/**
* 获取定时任务进程名称
*
* @return string
*/
public static function processName(): string
{
return str_replace(' ', "-", sprintf('%s@%s', static::getRule(), static::class));
}
public function __construct()
{
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
$this->lastError = [
'type' => $errno,
'message' => $errstr,
'file' => $errfile,
'line' => $errline,
];
print_r($this->lastError);
if (error_reporting() & $errno) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}
});
register_shutdown_function(function() {
$this->sendAlarm();
});
}
/**
* 获取定时任务的运行规则
*
* @return string
*/
public static function getRule(): string
{
$rule = defined('static::RULE') ? static::RULE : ''; // 默认以代码中的规则为准
$classNameArr = explode('\\', static::class);
if ($ruleENV = getenv('CRONTAB_RULE_' . $classNameArr[array_key_last($classNameArr)])) {
// 如果在配置中设置了定时任务规则, 则以配置中的为准
$rule = $ruleENV;
}
return $rule;
}
/**
* 进程退出回调
*
* @param Worker $worker
* @return void
*/
public function onWorkerStop(Worker $worker): void
{
$this->sendAlarm();
}
/**
* 发送定时任务进程退出告警
*
* @return void
*/
protected function sendAlarm(): void
{
if ($this->hasSendWorkerStopAlarm) return;
// 获取到last_error并打印
$error = error_get_last() ?: $this->lastError;
$errorDetail = '';
if ($error) {
$errorTypeName = match ($error['type']) {
E_ERROR => 'E_ERROR',
E_WARNING => 'E_WARNING',
E_PARSE => 'E_PARSE',
E_NOTICE => 'E_NOTICE',
E_CORE_ERROR => 'E_CORE_ERROR',
E_CORE_WARNING => 'E_CORE_WARNING',
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
E_USER_ERROR => 'E_USER_ERROR',
E_USER_WARNING => 'E_USER_WARNING',
E_USER_NOTICE => 'E_USER_NOTICE',
E_STRICT => 'E_STRICT',
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
default => 'UNKNOWN',
};
$errorDetail = <<<EOT
类型: {$errorTypeName}
信息: {$error['message']}
文件: {$error['file']}
行号: {$error['line']}
EOT;
}
try {
// (new WebHookQiWeiBot())->sendCrontabWorkerStoppedAlarm(static::processName(), $errorDetail);
echo date('Y-m-d H:i:s') . " Worker " . static::processName() . " 发生错误退出: \n" . $errorDetail . PHP_EOL;
$this->hasSendWorkerStopAlarm = true;
} catch (\Throwable) {}
}
}
已经用其他方案解决了
在业务层try catch 发通知,然后再throw出来
就是这样改的, 原有的业务代码都onWorkerStart换成了另外一个基类的抽象方法, 基类写一个onWorkerStart, 然后try catch这个抽象方法, 就是替换了100多处很多业务代码的方法名字