Workerman是什么?
Workerman是一款純PHP開發(fā)的開源高性能的PHP socket 服務(wù)框架。
Workerman不是重復(fù)造輪子,它不是一個MVC框架,而是一個更底層更通用的socket服務(wù)框架,你可以用它開發(fā)tcp代理、梯子代理、做游戲服務(wù)器、郵件服務(wù)器、ftp服務(wù)器、甚至開發(fā)一個php版本的redis、php版本的數(shù)據(jù)庫、php版本的nginx、php版本的php-fpm等等。Workerman可以說是PHP領(lǐng)域的一次創(chuàng)新,讓開發(fā)者徹底擺脫了PHP只能做WEB的束縛。實際上Workerman類似一個PHP版本的nginx,核心也是多進(jìn)程+Epoll+非阻塞IO。Workerman每個進(jìn)程能維持上萬并發(fā)連接。由于本身常住內(nèi)存,不依賴Apache、nginx、php-fpm這些容器,擁有超高的性能。同時支持TCP、UDP、UNIXSOCKET,支持長連接,支持Websocket、HTTP、WSS、HTTPS等通訊協(xié)議以及各種自定義協(xié)議。擁有定時器、異步socket客戶端、異步Mysql、異步Redis、異步Http、異步消息隊列等眾多高性能組件。
Workerman的一些應(yīng)用方向
Workerman不同于傳統(tǒng)MVC框架,Workerman不僅可以用于Web開發(fā),同時還有更廣闊的應(yīng)用領(lǐng)域,例如即時通訊類、物聯(lián)網(wǎng)、游戲、服務(wù)治理、其它服務(wù)器或者中間件,這無疑大大提高了PHP開發(fā)者的視野。目前這些領(lǐng)域的PHP開發(fā)者奇缺,如果想在PHP領(lǐng)域有自己的技術(shù)優(yōu)勢,不滿足于每天的增刪改查工作,或者想向架構(gòu)師方向或者技術(shù)大牛的方向發(fā)展,Workerman都是非常值得學(xué)習(xí)的框架。建議開發(fā)者不僅會用,而且能基于Workerman開發(fā)出屬于自己的開源項目,提升技能增加自己的影響力,比如Beanbun多進(jìn)程網(wǎng)絡(luò)爬蟲框架就是一個很好的例子,剛剛上線不久就獲得眾多好評。
Workerman的一些應(yīng)用方向如下:
1、即時通訊類
例如網(wǎng)頁即時聊天、即時消息推送、微信小程序、手機(jī)app消息推送、PC軟件消息推送等等
[示例 workerman-chat聊天室 、 web消息推送 、 小蝌蚪聊天室]
2、物聯(lián)網(wǎng)類
例如Workerman與打印機(jī)通訊、與單片機(jī)通訊、智能手環(huán)、智能家居、共享單車等等。
[客戶案例如 易聯(lián)云、易泊時代等]
3、游戲服務(wù)器類
例如棋牌游戲、MMORPG游戲等等。[示例 browserquest-php]
4、HTTP服務(wù)
例如 寫高性能HTTP接口、高性能網(wǎng)站。如果想要做HTTP相關(guān)的服務(wù)或者站點強(qiáng)烈推薦 webman
5、SOA服務(wù)化
利用Workerman將現(xiàn)有業(yè)務(wù)不同功能單元封裝起來,以服務(wù)的形式對外提供統(tǒng)一的接口,達(dá)到系統(tǒng)松耦合、易維護(hù)、高可用、易伸縮。[示例 workerman-json-rpc、 workerman-thrift]
6、其它服務(wù)器軟件
例如 GatewayWorker,PHPSocket.IO,http代理,sock5代理,分布式通訊組件,分布式變量共享組件,消息隊列、DNS服務(wù)器、WebServer、CDN服務(wù)器、FTP服務(wù)器等等
7、中間件
例如異步MySQL組件,異步redis組件,異步http組件,異步消息隊列組件,異步dns組件,文件監(jiān)控組件,還有很多第三方開發(fā)的組件框架等等
顯然傳統(tǒng)的mvc框架很難實現(xiàn)以上的功能,所以也就是workerman誕生的原因。
下面使用workerman建立websocket連接,實現(xiàn)一個簡單的聊天事例。
下載workerman源碼包
在start.php中引入源碼,將源碼包vendor目錄放在與start.php同級目錄
1. start.php 代碼如下:
<?php
require_once 'vendor/autoload.php';
use Workerman\Worker;
/**
* 多端口,多協(xié)議
*/
// 使用websocket協(xié)議
$ws_worker = new Worker("websocket://0.0.0.0:10086");
// 啟動進(jìn)程對外服務(wù)。如果進(jìn)程啟動后建立一個內(nèi)部通訊端口,則進(jìn)程數(shù)必須為1$ws_worker->count = 1;
// 連接事件,阻斷非法連接
$ws_worker->onConnect = function ($connection) use ($domains){
};
// 進(jìn)程啟動后建立一個內(nèi)部通訊端口
$ws_worker->onWorkerStart = function ($ws_worker){
$inner_text_worker = new Worker("Text://0.0.0.0:10087");
$inner_text_worker->onMessage = function ($connection, $buffer) use ($ws_worker)
{
$data = json_decode($buffer, true);
$id = $data['id'];
// 通過workerman向id的頁面推送數(shù)據(jù)
$ret = sendMessageById($id, $buffer);
// 返回推送結(jié)果
$connection->send($ret ? 'ok' : 'fail');
};
$inner_text_worker->listen();
$inner_text_worker = new Worker("http://0.0.0.0:10087");
$inner_text_worker->onMessage = function ($http) use ($ws_worker)
{
$id = $_POST['id'];
$status = $_POST['status'];
// 通過workerman向id的頁面推送數(shù)據(jù)
$ret = sendMessageById($id, ['id', 'status' => $status]);
// 返回推送結(jié)果
$http->send($ret ? 'ok' : 'fail');
};
$inner_text_worker->listen();};
// 增加一個屬性,用來保存uid到connection的映射
$ws_worker->uidConnetions = [];// 客戶端發(fā)送消息時回調(diào)
$ws_worker->onMessage = function ($connection, $data) use ($ws_worker){
// 判斷當(dāng)前客戶端是否已經(jīng)驗證過
if (!isset($connection->uid)) {
$connection->uid = $data;
$ws_worker->uidConnetions[$connection->uid] = $connection;
return;
}};
// 當(dāng)客戶端連接斷開時
$ws_worker->onClose = function ($connection) use ($ws_worker){
if (isset($connection->uid)) {
unset($ws_worker->uidConnetions[$connection->uid]);
}};
/**
* sendMessageById 向指定的客戶端推送數(shù)據(jù)
* @param mixed $id
* @param mixed $message
* @access public
* @return bool
*/
function sendMessageById($id, $message){
if (is_array($message))
$message = json_encode($message);
global $ws_worker;
if (isset($ws_worker->uidConnetions[$id])) {
$connection = $ws_worker->uidConnetions[$id];
$connection->send($message);
return true;
}
return false;}
/**
* broadcast 向所有用戶推送數(shù)據(jù)
* @param mixed $message
* @access public
* @return void
*/
function broadcast($message){
global $ws_worker;
foreach ($ws_worker->uidConnetions as $connection) {
$connection->send($message);
} }
Worker::runAll();
2.開啟服務(wù)進(jìn)程:php start.php start
3. 發(fā)送http請求到對應(yīng)的端口,代碼如下:
function post($url, $data = [], $timeout = 3){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); //設(shè)置超時
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$result = curl_exec($ch);
curl_close($ch);
return $result;}
}
// 發(fā)送http請求到http服務(wù)
$data['id'] = 100;$data['status'] = 6;
// 你的IP地址+端口號
$url = 'http://192.168.16.101:10087';
$result = post($url, $data);echo $result . "\n";
4.客戶端連接代碼:
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8">
<title>WebSocket_client</title>
<script>
//創(chuàng)建一個socket實例
ws = new WebSocket("ws://192.168.16.101:10086");
ws.onopen = function () {
console.log("連接成功");
ws.send(100);
//console.log("發(fā)送數(shù)據(jù):tom")
};
ws.onmessage = function (e) {
console.log("收到服務(wù)端信息:" + e.data);
};
</script>
</head>
<body>
</body>
</html>
crmeb知識付費(fèi)系統(tǒng)的直播功能就是使用了workerman+websocket功能實現(xiàn)的,application\push\controller\Push.php文件就是workerman的初始文件。public/wap/first/zsff/js/Websocket.js為websocket功能文件。如果需要二開直播間互動功能,可以通過這兩個文件修改。