workerman中捕获notice错误并抛出异常
在workerman中出现了notice错误并不会中断程序运行,这是抛出一个无关紧要的提示,这显然与我们的程序设计初衷不符。
例如我们去获取数组中的一个key-value值,当key不存在的时候只是提示一个notice,而我们要把这个弄成一个异常。
研究了一下,set_error_handler方法可以实现我们想要的功能,来看一下代码吧。
在events.php中的代码
<?php /** * 用于检测业务代码死循环或者长时间阻塞等问题 * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload * 然后观察一段时间workerman.log看是否有process_timeout异常 */ //declare(ticks=1); use App\Service\MemberService; use App\Service\InterviewService; use \GatewayWorker\Lib\Gateway; use think\facade\Db; use \App\Library\MyException; use \App\Library\Rdb; define('ping', 1);//ping 心跳 返回1 define('bind', 2);//bind 绑定 返回2 define('interview_admin_bind', 999);//集合室后台管理账户绑定 define('interview_member_join', 1001);//集合室-用户进入集合室 define('interview_member_leave', 1002);//集合室-用户离开集合室 define('interview_change_attribute', 1011);//集合室切换fixed 自由座位 和固定座位 define('interview_seat_order', 1012);//集合室座位排序 define('interview_seat_order_rand', 1013);//集合室座位排序-随机 define('interview_seat_change', 1014);//集合室座位排序-交换 define('interview_member_ban', 1015);//集合室-踢出用户 define('interview_member_voice', 1016);//集合室-用户禁言 define('interview_topic', 1017);//集合室出题 define('interview_voice_order', 1018);//传递麦序 define('interview_update', 1050);//集合室-更新频道属性 define('interview_send_msg', 1051);//集合室发送消息 class Events { use \App\Library\Tools; public static function onWorkerStart() { \set_error_handler(function ($type, $msg) { var_dump($type, $msg); throw new MyException(400, $msg); }, E_NOTICE); Db::setConfig(\App\Config\Config::$db_config); Rdb::getInstance()->flushAll(); } public static function onConnect($client_id) { } public static function onMessage($client_id, $message) { try { $message = json_decode($message, true); if (!is_array($message)) return Gateway::sendToCurrentClient(self::error(400, '参数类型错误')); if ($message['type'] > 1000 and !isset($_SESSION['member_id'])) { return Gateway::closeClient($client_id, self::error(400, '未登录')); } switch ($message['type']) { case interview_admin_bind: InterviewService::admin_bind($client_id, $message); break; case interview_voice_order: InterviewService::voice_order($client_id, $message); break; case interview_topic: InterviewService::topic($client_id, $message); break; case interview_member_voice: InterviewService::member_voice($client_id, $message); break; case interview_member_ban: InterviewService::member_ban($client_id, $message); break; case interview_seat_change: InterviewService::seat_change($client_id, $message); break; case interview_seat_order_rand: InterviewService::seat_order_rand($client_id, $message); break; case interview_seat_order: InterviewService::seat_order($client_id, $message); break; case interview_send_msg: InterviewService::send_msg($client_id, $message); break; case interview_change_attribute: InterviewService::change_attribute($client_id, $message); break; case interview_update: InterviewService::update($client_id, $message); break; case interview_member_leave: InterviewService::leave($client_id); break; case interview_member_join: InterviewService::join($client_id, $_SESSION['member_id'], $_SESSION['member'], $message); break; case bind: MemberService::bind($client_id, $message); break; default : return Gateway::sendToCurrentClient(self::success(1)); break; } } catch (Throwable $exception) { if ($exception instanceof MyException) { return Gateway::sendToCurrentClient(self::error($exception->type, $exception->getMessage(), $exception->getCode(), $exception->data)); } else { return Gateway::sendToCurrentClient(self::error(400, $exception->getMessage())); } } } public static function onClose($client_id) { InterviewService::leave($client_id); } }
Events.php文件主要看onMessage回调,我们通过传递给服务器的不同type参数作为路由凭据,然后调用不同的方法,处理业务。
在最外层有一个try catch来捕捉异常。注意是捕捉的Throwable 。这是Exception和Error两个不同异常的父类接口,但是并不包含notice和warning这类的错误,所以不能捕获。
所以一旦出现了这类错误并不会中断掉程序,那么要来判断例如key-value是否存在的这类情况就太痛苦了,到处都是isset。。。
public static function onWorkerStart() { \set_error_handler(function ($type, $msg) { var_dump($type, $msg); throw new MyException(400, $msg); }, E_NOTICE); Db::setConfig(\App\Config\Config::$db_config); Rdb::getInstance()->flushAll(); }
在来分析一下onWorkerStart()方法中,我们用set_error_handler来设置E_NOTICE类型的错误交给一个闭包来处理,该闭包抛出一个MyExcepetion异常,这个异常就是继承了Throwable接口,是可以被正常捕获的。同时中断程序。
