什么是事件?
一个特定的场景发生了一个特定的情况就是一个事件。
事件在设计中的作用
为对象之间解耦。
举例
现有用户中心和消息中心。
目前,有一个用户注册的场景,此场景要求用户注册成功后要给用户发送多渠道欢迎通知(微信、短信、邮件等)。
分析上述场景,用户注册由用户中心实现,通知由消息中心实现。场景的发起者由用户中心发起。
则形成一个流程
肯定是先想到,在用户注册逻辑中调用消息中心提供的接口。业务流程上来说是正确的,但是这里产生了一个问题,用户中心和消息中心 产生了一个耦合点。
此场景耦合问题说明
1、当消息中心的消息接口发生变化时,需要通知用户中心改变其调用结构(明明是对方自己的接口调用发生了改变,却让我来跟着变化,不合理)
2、当不止消息中心有用户注册场景需求的时候,需要用户中心再加一次其它服务中心的业务调用。(不合理)
如何解耦
为解决上述问题,事件机制可以作为一个处理手段。我们让双方中心抽象出一层。
用户中心 - 抽象出 用户注册成功事件
消息中心 - 抽象出 用户注册成功监听
当用户完成注册时,用户中心只负责抛出用户注册成功事件。由其他多个中心监听用户注册成功事件是否发生。当发生后,各自从事件中获取到用户对象去执行自己的逻辑。
好处
1、不用管其它业务中心的接口使用方法如何(由他们自己处理)
2、当有更多的业务中心需要监听用户注册时,只需要在事件注册树上声明监听和事件的关系即可,对用户中心是无感的。用户中心只做一件事件:抛出用户注册成功事件就可以了。
以下为根据方案设计自己制作的一套模型
Event 分发器
<?php
namespace TanYong\Event\Core;
trait EventDispatch
{
public static function dispatch(mixed ...$args) : void
{
$obj = (new \ReflectionClass(self::class))->newInstanceArgs($args);
Event::trigger($obj);
}
}
Event 注册树 及调度器
<?php
namespace TanYong\Event\Core;
/**
* 事件调度中心
*
*/
class Event
{
/**
* 事件注册树
*
* @var array
*/
public static array $mapEvent = [];
public static function listen(string $eventClass,array $closure) : void
{
if(isset(self::$mapEvent[$eventClass]))
{
array_push(self::$mapEvent[$eventClass],$closure);
}else{
self::$mapEvent[$eventClass] = [];
self::listen($eventClass,$closure);
}
}
public static function trigger(object $event) : void
{
$eventClass = (new \ReflectionObject($event))->getName();
if(isset(self::$mapEvent[$eventClass]))
{
foreach(self::$mapEvent[$eventClass] as $listenerInfo)
{
$listener = (new \ReflectionClass($listenerInfo[0]))->newInstance();
call_user_func([$listener,$listenerInfo[1]],$event);
}
}
}
}
事件 - 对象 - 规范化
<?php
namespace TanYong\Event\Core;
interface EventInterface
{
}
测试
<?php
namespace TanYong\Event\TestEvent;
use TanYong\Event\Core\EventDispatch;
use TanYong\Event\Core\EventInterface;
class Event1 implements EventInterface
{
use EventDispatch;
public string $name;
public int $age;
public function __construct(string $name,int $age)
{
$this->name = $name;
$this->age = $age;
}
}
<?php
namespace TanYong\Event\TestListener;
use TanYong\Event\Core\EventInterface;
class Listener1
{
public function run(EventInterface $event)
{
var_dump((new \ReflectionObject($event))->getName());
}
}
<?php
namespace TanYong\Event\TestListener;
use TanYong\Event\TestEvent\Event1;
class Listener2
{
public function run(Event1 $event1)
{
echo $event1->name;
}
}
index.php
<?php
require "vendor/autoload.php";
//注册事件
\TanYong\Event\Core\Event::listen(
\TanYong\Event\TestEvent\Event1::class,
[\TanYong\Event\TestListener\Listener1::class,'run']
);
\TanYong\Event\Core\Event::listen(
\TanYong\Event\TestEvent\Event1::class,
[\TanYong\Event\TestListener\Listener2::class,'run']
);
//触发Event1事件
\TanYong\Event\TestEvent\Event1::dispatch("谭勇",27);
以上,为单机版解耦
以微服务为例,如何做业务解耦
依托rabbitmq 消息队列实现解耦或是依托red pub/sub 模型解耦
本质就是消息分发,发布和监听的操作。
redis - pub/sub 模型
用户注册成功后,由用户中心发布消息至redis,此时监听着用户注册消息的消息中心和其它中心一旦捕获到用户注册成功的事件消息发生,就各自执行自己的业务。
RabbitMQ 消息队列
当用户注册发生后,会触发事件的生成,事件本身会向RabbitMQ队列推送需要告知的服务中心由哪些。达到解耦目的。