Laravel 回调系统的设计 Cybereits.com 白名单系统的设计
引
2017年12月末,Cybereits.com 项目上线,本拐和一众小伙伴奉命开发了白名单登计系统,在这篇文章中,本拐在以下几个点和各位看官爸爸做一下分享:
- 整体系统设计
- 环境的区分
- 回调机制的设计
整体设计
整体设计方式采用了原有的技术栈,即PHP/Laravel + RabbitMQ + Node.js 的形式。
整体系统如下:
其中,PHP/Laravel 提供核心服务,同时,为了保证系统在并发时的响应速度,采用了RabbitMQ 做为消息队列,即将长请求(三方API调用,邮件通知)做为事件Push到队列中,而Node.js 实现的Worker只负责将事件取出进行回调。
----以反射工厂形式实现的环境区分----
由于系统涉及到很多三方API,如短信,邮件,个人信息验证等,为了保证正式环境与测试环境的区分,采用工厂的形式的实现了这些API,以邮件部分为例,在创建邮件API接口时,采用了如下的实现:
private $_emaillogic = [
"local"=>\Cybereits\Modules\KYC\API\TestMail::class,
"testing"=>\Cybereits\Modules\KYC\API\TestMail::class,
"alpha"=>\Cybereits\Modules\KYC\API\TestMail::class,
"production"=>\Cybereits\Modules\KYC\API\SendCloud::class,
];
public function CreateSendMailLogic(){
return ReflectionHelper::CreateImplementsLogic($this->_emaillogic);
}
其中,ReflectionHelper::CreateImplementsLogic
是一个很简单的工厂实现,只是负责根据系统当前的环境配置返回对应的实现类:
class ReflectionHelper{
public static functionCreateImplementsLogic($env_array){
$env = config("app.env");
if(array_key_exists($env,$env_array)){
$class = $env_array[$env];
return new $class;
}
return null;
}
}
这样,根据相应lavrel 中.env 的环境配置,我们将不用环境下API的调用做了对应的区分。
----回调机制的设计----
-
事件的触发
这里的回调触发事件,典形的例子是,用户在请求验证码时,系统会向用户发送邮件,再比如,用户在注册成功时,会发送注册成功的邮件。
虽然不同的事件五花八门,但是总结下来,无外乎数据库的增,删,改操作。 而Laravel 中对数据库实现Model 已经有了封装的操作,因此,只要监听对应的Model的created,updated,deleted事件即可,同时,为了保证系统的效率,有相应的事件以后,系统只是将对应的事件push到消息对队列中。
为了实现系统的可扩展性,我们采用如下方式:
将所有要处理的事件都保存在配置文件中
底层实现一个observer,只在事件发生时将事件以 {event,model}的形式发送到相应的队列中。
底层实现一个observerLoader ,在appServiceProvider中进行调用,加载所有的事件
其中,配置文件示意如下:<?php return [ "\Cybereits\Modules\KYC\Model\EmailCheck"=>"\Cybereits\Common\Event\EventObserver", "\Cybereits\Modules\KYC\Model\EthAddress"=>"\Cybereits\Common\Event\EventObserver" ];
而observer 则也是一个很简单的操作。
class EventObserver
{
use RaiseEvent;
protected $event_queue=null;
public function created($data)
{
$this->_addToQueue($data,'created');
}
public function updated($data)
{
$this->_addToQueue($data,'updated');
}
public function deleted($data)
{
$this->_addToQueue($data,"deleted");
}
private function_addToQueue($data,$event,$messageType = "")
{
$model_class = get_class($data);
$queueData=(object)array();
$queueData->event = $event;
$queueData->time=date("Y-m-d H:i:s");
$queueData->model=$model_class;
$data->queueKey=$event;
$queueData->data=$data;
$queueData->messagetype=$messageType;
$queueData->source=null;
$this->AddQueueEvent($this->event_queue, $queueData);
}
}
这里用了 RaiseEvent 这个特性,设计这个特性有两个调用:
- AddQueueEvent ,在observer 的_addToQueue中调用,将所有的事件临时保存在一个数组中 。
- RaiseEvent , 在controller 基类中调用,将组中的事件依次push到消息队列中。
通过这种设计,将事件回调的处理与业务实现本身完全分隔开,达到系统的可配置 。 -
事件的回调
当事件被推送到队列中后,node.js 的worker 将事件从队列中取出,原封不动的回调给PHP服务,为了保证维护,所有的事件都回调一个PHP服务/api/event/queuecallback
当api/event/queuecallback 被调用时,收到的信息有:
发生的模型 class ,2. 模型的数据 data ,3. 模型操作的事件 event
这时callback 只要根据这三种数据找到对应的回调处理类进行处理即可。
在回调处理类上,与触发类型,我们做了如下工作:
所有要处理的事件与处理类的对应关系都保存在配置文件中
实现了一个从配置文件加载处理类并处理回调业务的Handler类
为了为方便,我们设计了一个基本的Ihandler 接口。
其中,配置文件如下示:<?php return [ "Cybereits\Modules\KYC\Model\EmailCheck"=>[ "created"=>[ "\Cybereits\Modules\KYC\Handler\SendMail", ], ], "Cybereits\Modules\KYC\Model\EthAddress"=>[ "created"=>[ "\Cybereits\Modules\KYC\Handler\SendRegMail" ] ] ];
注意,这里与event 的不同,我们在这里设置了Model->event ->handler 的三级配置.
加载回调的类也很简单:
class EventHandler
{
public function Handle($queueData)
{
$event = $queueData->event;
$model = $queueData->model;
$data = $queueData->data;
$cfg = config("handler");
if (array_key_exists($model, $cfg) === true) {
$setting = $cfg [$model];
if (array_key_exists($event, $setting) === true) {
$handleClasses = $setting [$event];
foreach ($handleClasses as $handleClass) {
$interface = class_implements($handleClass);
if(array_key_exists("Cybereits\Common\Event\IHandleLogic", $interface)) {
$handleObj = new$handleClass;
$handleObj -> Handle($data);
}
}
}
}
}
}
那么,以配置文件中的SendMail为例,我们的回调就会变的很简单:
class SendMail implements IHandleLogic
{
public function Handle($data)
{
$mail = $data["email"];
$code = $data["checkcode"];
$fac = new APIFactory();
$sendCloud = $fac->CreateSendMailLogic();
$sendCloud->SendCheckCodeEmail($mail, $code);
}
}
通过这种机制,我们将事件的触发,回调做了完全的分离,在这个应用下,看似有些过渡设计,实际上,这是一种非常高效并且易于维护的设计,因为咔咔买房的服务部分,好多逻辑都是使用这种方式进行的解耦。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: