摘要:然而盡管如此,很多人可能都沒(méi)有思考過(guò),如何優(yōu)雅的寫出自己的物聯(lián)網(wǎng)服務(wù)器。
PHP不適合做物聯(lián)網(wǎng)服務(wù)端嗎?
在傳統(tǒng)的思維中,經(jīng)常會(huì)有人告訴你,php不適合用來(lái)做物聯(lián)網(wǎng)服務(wù)端,讓你換java,node,go等其他語(yǔ)言,是的,沒(méi)錯(cuò)傳統(tǒng)意義上的php,確實(shí)很難做物聯(lián)網(wǎng)服務(wù)器,因?yàn)樗鼘?shí)在太蹩腳了,當(dāng)然,這也不是意味著徹底就不能做。舉個(gè)例子,當(dāng)你想實(shí)現(xiàn)一個(gè)TCP服務(wù)器的時(shí)候,你可能需要寫出原理大約如下的代碼:
for ($i = 0;$i <= 1;$i++){ $pid = pcntl_fork(); if($pid){ if($i == 0){ $server = stream_socket_server("tcp://127.0.0.1:9501", $errno, $errstr, STREAM_SERVER_BIND); }else if($i == 1){ $tickTime = time()+3600; while (1){ usleep(1); if($tickTime == time()){ //do my tick func } } } } }
以上代碼的意義等于在一個(gè)進(jìn)程中創(chuàng)建一個(gè)TCP 服務(wù)端,另外一個(gè)進(jìn)程中死循環(huán)來(lái)做時(shí)間檢測(cè),從而實(shí)現(xiàn)定時(shí)器邏輯。這樣看起來(lái),確實(shí)很蹩腳,而且對(duì)于編程基礎(chǔ)普遍比較薄弱的PHPer來(lái)說(shuō),這真的很難維護(hù)。當(dāng)然這個(gè)時(shí)候,就會(huì)有人說(shuō),這不是還有Workerman嗎,是的,確實(shí)還有Workerman,Workerman就是高度封裝了上述代碼原理,幫助你專心于實(shí)現(xiàn)代碼邏輯的一個(gè)PHP多進(jìn)程框架,因此說(shuō)PHP不時(shí)候做物聯(lián)網(wǎng),其實(shí)這是謬論。當(dāng)然這個(gè)時(shí)候可能又會(huì)有人說(shuō),go語(yǔ)言有協(xié)程,你用Workerman當(dāng)出現(xiàn)阻塞數(shù)據(jù)庫(kù)調(diào)用的時(shí)候,那效率就非常的差,很難出現(xiàn)高并發(fā),這么說(shuō)沒(méi)錯(cuò),但是實(shí)際上,我們可以盡可能的用多進(jìn)程去彌補(bǔ)這個(gè)不足,也就是堆機(jī)器。當(dāng)然,如果你真的想錙銖必較,沒(méi)關(guān)系,這個(gè)時(shí)候我們就可以拿出我們的殺器,那就是Swoole4.x的協(xié)程。
Swoole做TCP服務(wù)器舉個(gè)例子,如下代碼:
$server = new swoole_server("127.0.0.1", 9501); $server->on("workerstart",function ($ser,$workerId){ if($workerId == 0){ swoole_timer_tick(1000,function (){ }); } }); $server->on("connect", function ($server, $fd){ echo "connection open: {$fd} "; }); $server->on("receive", function ($server, $fd, $reactor_id, $data) { $server->send($fd, "Swoole: {$data}"); $server->close($fd); }); $server->on("close", function ($server, $fd) { echo "connection close: {$fd} "; }); $server->start();
我們就可以很快的創(chuàng)建出一個(gè)多進(jìn)程的協(xié)程TCP服務(wù)器,而且在各個(gè)回調(diào)函數(shù)內(nèi),均自動(dòng)創(chuàng)建協(xié)程環(huán)境,我們可以在協(xié)程回調(diào)內(nèi),去調(diào)用協(xié)程的數(shù)據(jù)庫(kù)API,這樣就避免了因?yàn)樽枞麛?shù)據(jù)庫(kù)調(diào)用而導(dǎo)致無(wú)法處理其他客戶端請(qǐng)求的問(wèn)題。然而盡管如此,很多人可能都沒(méi)有思考過(guò),如何優(yōu)雅的寫出自己的物聯(lián)網(wǎng)服務(wù)器。舉個(gè)例子,我們常見(jiàn)的互聯(lián)網(wǎng)設(shè)備管理服務(wù)中,大約可能出現(xiàn)如下代碼:
swoole_timer_tick(5000,function (){ $deviceList = $db->getAll(); foreach ($deviceList as $device){ //do your check /* * 例如設(shè)備狀態(tài)處于1,那么需要處理流程1 * 例如設(shè)備狀態(tài)處于2,那么需要處理流程2 * 例如設(shè)備狀態(tài)處于3,那么需要處理流程3 */ } });
定時(shí)遍歷檢查設(shè)備狀態(tài)以及廣播Actor模型
這樣乍一看好像無(wú)傷大雅,但是當(dāng)出現(xiàn)多種設(shè)備,且每種設(shè)備邏輯都不一致的時(shí)候,那么這樣的編寫模式就很容易寫出一大坨代碼出來(lái),而且在協(xié)程下,如果不注意變量訪問(wèn)安全與協(xié)程上下文隔離,那么就很容易出現(xiàn)bug,導(dǎo)致很難維護(hù)。
什么是Actor,簡(jiǎn)單來(lái)說(shuō),Actor就是一種高度抽象化的并發(fā)模型,每個(gè)Actor實(shí)例的內(nèi)存空間都是互相隔離的,用于降低用戶編程與維護(hù)難度。關(guān)于Swoole4.x如何實(shí)現(xiàn)協(xié)程版本的Actor,我們之前已經(jīng)在文章 https://segmentfault.com/a/11... 中講解了如何用Swoole實(shí)現(xiàn)協(xié)程的原理。
Actor模型庫(kù)實(shí)戰(zhàn)我們依舊用easyswoole/actor庫(kù)來(lái)講解,例如,我們有一種型號(hào)的設(shè)備,那么我們可以定義一個(gè)設(shè)備Actor,并把該設(shè)備的全部邏輯,寫在該actor模型內(nèi),例子代碼如下:
namespace AppDevice; use EasySwooleActorAbstractActor; use EasySwooleActorActorConfig; use EasySwooleEasySwooleLogger; use EasySwooleEasySwooleServerManager; use EasySwooleEasySwooleTrigger; class DeviceActor extends AbstractActor { private $fd; private $deviceId; private $lastHeartBeat; public static function configure(ActorConfig $actorConfig) { $actorConfig->setActorName("Device"); } protected function onStart() { $this->lastHeartBeat = time(); /* * 該參數(shù)是創(chuàng)建的時(shí)候傳遞的 */ $this->fd = $this->getArg()["fd"]; $this->deviceId = $this->getArg()["deviceId"]; //記錄到table manager中 DeviceManager::addDevice(new DeviceBean([ "deviceId"=>$this->deviceId, "actorId"=>$this->actorId(), "fd"=>$this->fd ])); //推送消息 ServerManager::getInstance()->getSwooleServer()->push($this->fd,"connect to server success,your actorId is {$this->actorId()}"); //創(chuàng)建一個(gè)定時(shí)器,如果一個(gè)設(shè)備20s沒(méi)有收到消息,自動(dòng)下線 $this->tick(20*2000,function (){ if(time() - $this->lastHeartBeat > 20){ $this->exit(-1); } }); } protected function onMessage($msg) { if($msg instanceof Command){ switch ($msg->getCommand()){ case $msg::RECONNECT:{ DeviceManager::updateDeviceInfo($this->deviceId,[ "fd"=>$msg->getArg() ]); $this->fd = $msg->getArg(); Logger::getInstance()->console("deviceId {$this->deviceId} at actorId {$this->actorId()} reconnect success"); ServerManager::getInstance()->getSwooleServer()->push($this->fd,"deviceId {$this->deviceId} at actorId {$this->actorId()} reconnect success"); break; } case $msg::WS_MSG:{ $recv = $msg->getArg(); Logger::getInstance()->console("deviceId {$this->deviceId} at actorId {$this->actorId()} recv ws msg: {$recv}"); ServerManager::getInstance()->getSwooleServer()->push($this->fd,"actor recv msg for hash ".md5($recv)); break; } case $msg::REPLY_MSG:{ $recv = $msg->getArg(); Logger::getInstance()->console("deviceId {$this->deviceId} at actorId {$this->actorId()} recv reply msg: {$recv}"); ServerManager::getInstance()->getSwooleServer()->push($this->fd,"actor recv reply msg ".$recv); //此處return 一個(gè)數(shù)據(jù),會(huì)返回給客戶端 return "actorId {$this->actorId()} recv {$recv}"; break; } } } } protected function onExit($arg) { if($arg == -1){ if(ServerManager::getInstance()->getSwooleServer()->exist($this->fd)){ ServerManager::getInstance()->getSwooleServer()->push($this->fd,"heartbeat lost,actor exit"); ServerManager::getInstance()->getSwooleServer()->close($this->fd); } } DeviceManager::deleteDevice($this->deviceId); Logger::getInstance()->console("deviceId {$this->deviceId} at actorId {$this->actorId()} exit"); } protected function onException(Throwable $throwable) { Trigger::getInstance()->throwable($throwable); } }
在該Actor內(nèi),我們定義了這個(gè)設(shè)備的生命周期行為。
設(shè)備上線,記錄設(shè)備id與fd信息,并創(chuàng)建心跳周期檢查
收到消息,可以對(duì)該Actor投遞數(shù)據(jù),處理對(duì)應(yīng)的消息行為
設(shè)備下線,當(dāng)設(shè)備下線,可以自動(dòng)的清理定時(shí)器與其他的一些通知與清理邏輯
我們可以很清楚的看到,Actor模型下,允許我們對(duì)一種設(shè)備模型進(jìn)行高度自治的管理。當(dāng)然,我們本章節(jié)主要在講解如何優(yōu)雅的利用Swoole協(xié)程來(lái)實(shí)現(xiàn)Actor模型,從而更好的開發(fā)管理我們的設(shè)備,因此我不再貼過(guò)多的代碼,有興趣的同學(xué)可以在Easyswoole框架demo中查看完整的示例代碼https://github.com/easy-swool...
Easyswoole項(xiàng)目主頁(yè):http://easyswoole.com/
Easyswoole github 主倉(cāng)庫(kù)https://github.com/easy-swool... ,如果你覺(jué)得我們的努力有對(duì)你起到幫助作用,記得給個(gè)star
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/31668.html
摘要:新增新增模型方法,主動(dòng)刷新數(shù)據(jù)表結(jié)構(gòu)緩存。分布式并發(fā)模型是什么是一種與共享內(nèi)存對(duì)應(yīng)的并發(fā)模型,具有資源獨(dú)占性。都分布在不同的機(jī)器上。 One - 極簡(jiǎn) . 高性能 . 松耦合 . 分布式 . 可運(yùn)行于多種環(huán)境(cli,apache/php-fpm,swoole) 碼云: https://gitee.com/vicself/onegithub: https://github.com/li...
摘要:如需了解更多物聯(lián)網(wǎng)網(wǎng)絡(luò)編程知識(shí)請(qǐng)點(diǎn)擊物聯(lián)網(wǎng)云端開發(fā)武器庫(kù)物聯(lián)網(wǎng)高并發(fā)編程之網(wǎng)絡(luò)編程中的線程模型值得說(shuō)明的是,具體選擇線程還是進(jìn)程,更多是與平臺(tái)及編程語(yǔ)言相關(guān)。 如需了解更多物聯(lián)網(wǎng)網(wǎng)絡(luò)編程知識(shí)請(qǐng)點(diǎn)擊:物聯(lián)網(wǎng)云端開發(fā)武器庫(kù) 物聯(lián)網(wǎng)高并發(fā)編程之網(wǎng)絡(luò)編程中的線程模型 值得說(shuō)明的是,具體選擇線程還是進(jìn)程,更多是與平臺(tái)及編程語(yǔ)言相關(guān)。例如 C 語(yǔ)言使用線程和進(jìn)程都可以(例如 Nginx 使用進(jìn)程...
摘要:協(xié)程與信箱得益于,我們可以基于的協(xié)程與快速實(shí)現(xiàn)一個(gè)信箱模式調(diào)度。樣例代碼比如在一個(gè)聊天室中,我們可以定義一個(gè)房間模型。 什么是Actor? Actor對(duì)于PHPer來(lái)說(shuō),可能會(huì)比較陌生,寫過(guò)Java的同學(xué)會(huì)比較熟悉,Java一直都有線程的概念(雖然PHP有Pthread,但不普及),它是一種非共享內(nèi)存的并發(fā)模型,每個(gè)Actor內(nèi)的數(shù)據(jù)獨(dú)立存在,Actor之間通過(guò)消息傳遞的形式進(jìn)行交互調(diào)...
摘要:原文鏈接解決了什么問(wèn)題使用模型來(lái)克服傳統(tǒng)面向?qū)ο缶幊棠P偷木窒扌?,并?yīng)對(duì)高并發(fā)分布式系統(tǒng)所帶來(lái)的挑戰(zhàn)。在某些情況,這個(gè)問(wèn)題可能會(huì)變得更糟糕,工作線程發(fā)生了錯(cuò)誤但是其自身卻無(wú)法恢復(fù)。 這段時(shí)間由于忙畢業(yè)前前后后的事情,拖更了很久,表示非常抱歉,回歸后的第一篇文章主要是看到了Akka最新文檔中寫的What problems does the actor model solve?,閱讀完后覺(jué)...
閱讀 1147·2021-10-08 10:04
閱讀 3592·2021-08-05 10:01
閱讀 2353·2019-08-30 11:04
閱讀 1854·2019-08-29 15:29
閱讀 951·2019-08-29 15:12
閱讀 1743·2019-08-26 12:11
閱讀 3181·2019-08-26 11:33
閱讀 1218·2019-08-26 10:23