Hyperf-事件机制+异常处理

标签(空格分隔): php, hyperf

异常处理器

    在 Hyperf 里,业务代码都运行在 Worker 进程 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 Worker 进程 被中断退出,这对服务而言也是不能接受的,捕获异常并输出合理的报错内容给客户端也是更加友好的。
我们可以通过对各个 server 定义不同的 异常处理器(ExceptionHandler),一旦业务流程存在没有捕获的异常,都会被传递到已注册的 异常处理器(ExceptionHandler) 去处理。

异常记录到日志

// 日志类

    <?php
declare(strict_types=1); namespace App\Utils; class NativeLog
{
private $dirPath; public function __construct()
{
$this->dirPath = BASE_PATH . '/runtime/logs/' . date("Ymd") . '/';
if (!is_dir($this->dirPath)) {
mkdir($this->dirPath, 0777, true);
}
} public function error(string $data) : bool
{
$data = "[ ERROR ] [" . date("Y-m-d H:i:s") . "] " . $data . PHP_EOL;
file_put_contents($this->dirPath . "error.log", $data, FILE_APPEND);
return true;
} }

hyperf 本身就实现了异常类的接管,如果有异常会打印到控制台输出。

// 增加异常信息的记录日志

/**
* 记录文本异常日志
* @param Throwable $throwable
*/
public function writeLog(Throwable $throwable)
{
$AppExceptionLog['server'] = "http";
$AppExceptionLog['method'] = $this->request->getMethod();
$AppExceptionLog['path'] = $this->request->url();
$AppExceptionLog['params'] = $this->request->all();
$AppExceptionLog['file'] = $throwable->getFile();
$AppExceptionLog['line'] = $throwable->getLine();
$AppExceptionLog['message'] = $throwable->getMessage();
(new NativeLog())->error(json_encode($AppExceptionLog));
}

日志记录效果

但是有个问题,try catch 捕获的代码如果有异常就不会记录

引入事件机制

    事件模式是一种经过了充分测试的可靠机制,是一种非常适用于解耦的机制,分别存在以下 3 种角色:

事件(Event) 是传递于应用代码与 监听器(Listener) 之间的通讯对象
监听器(Listener) 是用于监听 事件(Event) 的发生的监听对象
事件调度器(EventDispatcher) 是用于触发 事件(Event) 和管理 监听器(Listener) 与 事件(Event) 之间的关系的管理者对象
用通俗易懂的例子来说明就是,假设我们存在一个 UserService::register() 方法用于注册一个账号,在账号注册成功后我们可以通过事件调度器触发 UserRegistered 事件,由监听器监听该事件的发生,在触发时进行某些操作,比如发送用户注册成功短信,在业务发展的同时我们可能会希望在用户注册成功之后做更多的事情,比如发送用户注册成功的邮件等待,此时我们就可以通过再增加一个监听器监听 UserRegistered 事件即可,无需在 UserService::register() 方法内部增加与之无关的代码。

代码示例

// 新增异常事件

<?php

declare(strict_types=1);

namespace App\Event;

/**
* 系统异常事件
* Class AppException
* @package App\Event
*/
class AppException
{
public $throwable; public function __construct($throwable)
{
$this->throwable = $throwable;
}
} // 新增异常事件监听器 <?php declare(strict_types=1); namespace App\Listener; use App\Event\AppException;
use App\Utils\NativeLog;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\HttpServer\Contract\RequestInterface; /**
* 异常事件监听器
* Class AppExceptionListener
* @package App\Listener
* @Listener()
*/
class AppExceptionListener implements ListenerInterface
{ /**
* @Inject()
* @var RequestInterface
*/
private $request; /**
* @Inject()
* @var NativeLog
*/
private $nativeLog; /**
* @inheritDoc
*/
public function listen(): array
{
// TODO: Implement listen() method.
// 返回一个该监听器要监听的事件数组,可以同时监听多个事件
return [
AppException::class,
];
} /**
* @param object $throwable
*/
public function process(object $throwable)
{
$throwable = $throwable->throwable;
$request = $this->request;
$AppExceptionLog['server'] = "http";
$AppExceptionLog['method'] = $request->getMethod();
$AppExceptionLog['path'] = $request->url();
$AppExceptionLog['params'] = $request->all();
$AppExceptionLog['file'] = $throwable->getFile();
$AppExceptionLog['line'] = $throwable->getLine();
$AppExceptionLog['message'] = $throwable->getMessage();
$this->nativeLog->error(json_encode($AppExceptionLog));
}
} // 控制器 <?php declare(strict_types=1); namespace App\Admin\Controller; use App\Admin\Model\UserModel;
use App\Admin\Service\UserService;
use App\Event\AppException;
use App\Rpc\Inter\UserServiceInter;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\Utils\Context;
use Psr\EventDispatcher\EventDispatcherInterface;
use function _HumbugBoxa5be08ba8ddb\React\Promise\Stream\first; /**
* 用户控制器
* Class UserController
* @package App\Admin\Controller
* @AutoController()
*/
class UserController extends AdminBaseController
{
/**
* @Inject
* @var EventDispatcherInterface
*/
private $eventDispatcher; /**
* 异常测试
*/
public function exception()
{
try {
array_column();
} catch (\Throwable $throwable) {
$this->eventDispatcher->dispatch(new AppException($throwable));
}
}

Hyperf-事件机制+异常处理的更多相关文章

  1. 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)

    前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜鸟,建议您好好读一读,真的理解下来会有不一样的收获 在下才疏学浅, ...

  2. tkinter事件机制

    一.tkinter.Event tkinter的事件机制跟js是一样的,也是只有一个Event类,这个类包罗万象,集成了键盘事件,鼠标事件,包含各种参数. 不像java swing那种强类型事件,sw ...

  3. [解惑]JavaScript事件机制

    群里童鞋问到关于事件传播的一个问题:“事件捕获的时候,阻止冒泡,事件到达目标之后,还会冒泡吗?”. 初学 JS 的童鞋经常会有诸多疑问,我在很多 QQ 群也混了好几年了,耳濡目染也也收获了不少,以后会 ...

  4. Atitit  数据库的事件机制--触发器与定时任务attilax总结

    Atitit  数据库的事件机制--触发器与定时任务attilax总结 1.1. 事件机制的图谱1 2. 触发器的类型2 3. 实现原理 After触发器 Vs Instead Of触发器2 3.1. ...

  5. 深入浅出iOS事件机制

    原文地址: http://zhoon.github.io/ios/2015/04/12/ios-event.html 本文章将讲解有关iOS事件的传递机制,如有错误或者不同的见解,欢迎留言指出. iO ...

  6. Java 事件机制

    java事件机制包括三个部分:事件.事件监听器.事件源. 1.事件.一般继承自java.util.EventObject类,封装了事件源对象及跟事件相关的信息,用于listener的相应的方法之中,作 ...

  7. Angular $scope和$rootScope事件机制之$emit、$broadcast和$on

    Angular按照发布/订阅模式设计了其事件系统,使用时需要“发布”事件,并在适当的位置“订阅”或“退订”事件,就像邮箱里面大量的订阅邮件一样,当我们不需要时就可以将其退订了.具体到开发中,对应着$s ...

  8. JavaScript 详说事件机制之冒泡、捕获、传播、委托

    DOM事件流(event  flow )存在三个阶段:事件捕获阶段.处于目标阶段.事件冒泡阶段. 事件捕获(event  capturing):通俗的理解就是,当鼠标点击或者触发dom事件时,浏览器会 ...

  9. DOM事件机制进一步理解

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

随机推荐

  1. Kibana,Logstash 和 Cerebro 的安装运行

    公号:码农充电站pro 主页:https://codeshellme.github.io 1,安装 Kibana Kibana 用于数据可视化,我们可以进入到 Kibana 下载页面下载 Kibana ...

  2. 2019牛客暑期多校训练营(第三场)G.Removing Stones(ST+分治)

    题意:给你n堆石子 每堆有ai个 现在问你有多少个连续的区间保证最大值小于等于该区间和的两倍 思路:我们可以考虑每个区间的分割点 总是该区间的最大值 所以我们只要ST找到该区间的最大值 然后每次都枚举 ...

  3. POJ 2594 Treasure Exploration 最小可相交路径覆盖

    最小路径覆盖 DAG的最小可相交路径覆盖: 算法:先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b.然后就转化成了最小不相交路径覆盖问题. 这里解释一下floyd的作用如 ...

  4. SPOJ 227 Ordering the Soldiers

    As you are probably well aware, in Byteland it is always the military officer's main worry to order ...

  5. zjnu1181 石子合并【基础算法・动态规划】——高级

    Description 在操场上沿一直线排列着  n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的两堆石子合并成新的一堆,  并将新的一堆石子数记为该次合并的得分.允许在第一次合并前对调一 ...

  6. MySQL 事务日志

    重做日志(Redo log) 重做日志(Redo log),也叫做前滚日志,存放在如下位置,轮询使用,记录着内存中数据页的变化,在事务 ACID 过程中,主要实现的是 D(Durability)的作用 ...

  7. 数理统计10(习题篇):寻找UMVUE

    利用L-S定理,充分完备统计量法是寻找UMVUE的最方便方法,不过实际运用时还需要一些小技巧,比如如何写出充分完备统计量.如何找到无偏估计.如何求条件期望,等等.课本上的例题几乎涵盖了所有这些技巧,我 ...

  8. WSL (Windows Subsystem for Linux) 的 VSLAM (Visual Simultaneous Localization and Mapping) 道路

    WSL 的 VSLAM 道路 以 Windows Subsystem for Linux 闯入 Visual Simultaneous Localization and Mapping 世界的艰难道路 ...

  9. Vmware 15.5 ubuntu 12.04.5-desktop-i386.iso insmod后死机

    就是makefile没有问题,在其他同学的相同环境下也没有问题,但是在我的虚拟机里就会死机,复制了其他同学的虚拟机过来也会死机,所以猜想是VMware的问题. 于是下载了Virtual box,然后安 ...

  10. C# 类 (8) - 抽象方法

    抽象 抽象方法 只能 定义在抽象类 里,并且抽象方法里没有具体的代码,像这种 为啥要定义一个空空如也的函数呢?这是为了用来约束 它的派生类 的行为, 这个例子,建立了一个数组,放了cat和dog,这两 ...