引
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 協議》,轉載必須註明作者和本文連結