在其它項目項目中主動推送消息
需求
有時候需要在非GatewayWorker環(huán)境中向客戶端推送數(shù)據(jù)。例如在一個普通的Web項目中通過GatewayWorker推送數(shù)據(jù)(前提是已經(jīng)部署了GatewayWorker,客戶端已經(jīng)連接GatewayWorker)。目前有三種比較方便方法推送數(shù)據(jù)。
方法一、使用GatewayClient客戶端推送,接口與GatewayWorker中的接口一致
客戶端源碼:
https://github.com/walkor/GatewayClient
GatewayWorker1.0請使用1.0版本的GatewayClient
GatewayWorker2.0.1-2.0.4請使用2.0.4版本的GatewayClient
GatewayWorker2.0.5-2.0.6版本請使用2.0.6版本的GatewayClient
GatewayWorker2.0.7版本請使用 2.0.7版本的GatewayClient
GatewayWorker3.0.0及以上版本請使用 3.0.0版本的GatewayClient
GatewayWorker3.0.8及以上版本請使用 3.0.13及以上版本的GatewayClient
注意:GatewayClient3.0.0開始支持composer并加了命名空間GatewayClient
查看GatewayWorker版本方法請點擊點擊這里
注意:
-
下載后GatewayClient目錄可以放在項目中的任意位置,只要項目能引用到GatewayClient/Gateway.php即可。
-
如果GatewayClient和GatewayWorker不是在同一臺服務(wù)器上,則需要先將start_gateway.php中的lanIp改成當前服務(wù)器的內(nèi)網(wǎng)ip(如果不在一個內(nèi)網(wǎng)可改成公網(wǎng)ip,需要GatewayWorker版本>=v3.0.22)。注意,無論何時lanIp都不能寫成
0.0.0.0
,否則將無法通訊。
注意
GatewayClient跨公網(wǎng)通訊需要GatewayWorker版本>=v3.0.22
跨公網(wǎng)通訊有風險,建議都放在一個內(nèi)網(wǎng)。
-
如果GatewayClient和GatewayWorker不是在同一臺服務(wù)器上,還要設(shè)置防火墻(云服務(wù)器的話還要設(shè)置安全組)讓以下端口可以被GatewayClient所在服務(wù)器訪問:1、start_gateway.php中的$gateway->startPort起始的幾個端口(要開放的端口個數(shù)和$gateway->count有關(guān))。2、start_register.php中的Register服務(wù)端口
反之如果GatewayClient和GatewayWorker在同一臺服務(wù)器上運行,則不用做任何更改,直接按照示例使用GatewayClient即可。 -
通過GatewayClient發(fā)送的數(shù)據(jù)不會經(jīng)過Event.php,而是直接經(jīng)由Gateway進程轉(zhuǎn)發(fā)給客戶端。
-
GatewayClient無法接收客戶端發(fā)來的數(shù)據(jù)。
-
GatewayClient無法直接用
$_SESSION
變量來操作GatewayWorker的session,但可以用Gateway::setSession/getSession/updateSession
等接口操作GatewayWorker的session。客戶端使用示例
require_once '/your/path/GatewayClient/Gateway.php';
/**
* gatewayClient 3.0.0及以上版本加了命名空間
* 而3.0.0以下版本不需要use GatewayClient\Gateway;
**/
use GatewayClient\Gateway;
/**
*====這個步驟是必須的====
*這里填寫Register服務(wù)的ip和Register端口,注意端口不是gateway端口
*ip不能是0.0.0.0,端口在start_register.php中可以找到
*這里假設(shè)GatewayClient和Register服務(wù)都在一臺服務(wù)器上,ip填寫127.0.0.1。
*如果不在一臺服務(wù)器則填寫真實的Register服務(wù)的內(nèi)網(wǎng)ip(或者外網(wǎng)ip)
**/
Gateway::$registerAddress = '127.0.0.1:1236';
// 以下是調(diào)用示例,接口與GatewayWorker環(huán)境的接口一致
// 注意除了不支持sendToCurrentClient和closeCurrentClient方法
// 其它方法都支持
Gateway::sendToAll($data);
Gateway::sendToClient($client_id, $data);
Gateway::closeClient($client_id);
Gateway::isOnline($client_id);
Gateway::bindUid($client_id, $uid);
Gateway::isUidOnline($uid);
Gateway::getClientIdByUid($client_id);
Gateway::unbindUid($client_id, $uid);
Gateway::sendToUid($uid, $data);
Gateway::joinGroup($client_id, $group);
Gateway::sendToGroup($group, $data);
Gateway::leaveGroup($client_id, $group);
Gateway::getClientCountByGroup($group);
Gateway::getClientSessionsByGroup($group);
Gateway::getAllClientCount();
Gateway::getAllClientSessions();
Gateway::setSession($client_id, $session);
Gateway::updateSession($client_id, $session);
Gateway::getSession($client_id);
...
方法二、用一個特殊的賬號當做管理客戶端,通過這個賬號推送數(shù)據(jù)
此方法通俗易懂,可以通過現(xiàn)有客戶端直接操作,具體代碼根據(jù)自己的業(yè)務(wù)實現(xiàn)
方法三、開啟一個內(nèi)部Gateway端口,用于推送數(shù)據(jù)
方法二雖然簡單,但是局限于只能通過客戶端界面操作,定時推送等需求不好直接操作客戶端,而通過PHP模擬客戶端可能會受到復雜協(xié)議的限制不好操作,這時我們可以開啟一個內(nèi)部文本協(xié)議的Gateway端口,通過PHP代碼使用文本協(xié)議連接WorkerMan作為客戶端向其它客戶端推送數(shù)據(jù)。
示例(workerman-chat為例)
服務(wù)端:
新建文件Applications/Chat/start_text_gateway.php,用于增加一個文本協(xié)議Gateway端口,內(nèi)容如下
<?php
use \Workerman\Worker;
use \GatewayWorker\Gateway;
use \Workerman\Autoloader;
require_once __DIR__ . '/../../Workerman/Autoloader.php';
Autoloader::setRootPath(__DIR__);
// #### 內(nèi)部推送端口(假設(shè)當前服務(wù)器內(nèi)網(wǎng)ip為192.168.100.100) ####
// #### 端口不能與原來start_gateway.php中一樣 ####
$internal_gateway = new Gateway("Text://192.168.100.100:7273");
$internal_gateway->name='internalGateway';
// #### 不要與原來start_gateway.php的一樣####
// #### 比原來跨度大一些,比如在原有startPort基礎(chǔ)上+1000 ####
$internal_gateway->startPort = 3300;
// #### 這里設(shè)置成與原start_gateway.php 一樣 ####
$internal_gateway->registerAddress = '127.0.0.1:1236';
// #### 內(nèi)部推送端口設(shè)置完畢 ####
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
客戶端:
在其它項目中就可以直接用PHP socket 使用文本協(xié)議調(diào)用,代碼類似如下:
// 建立連接,@see https://php.net/manual/zh/function.stream-socket-client.php
$client = stream_socket_client('tcp://192.168.100.100:7273');
if(!$client)exit("can not connect");
// 模擬超級用戶,以文本協(xié)議發(fā)送數(shù)據(jù),注意Text文本協(xié)議末尾有換行符(發(fā)送的數(shù)據(jù)中最好有能識別超級用戶的字段),這樣在Event.php中的onMessage方法中便能收到這個數(shù)據(jù),然后做相應(yīng)的處理即可
fwrite($client, '{"type":"send","content":"hello all", "user":"admin", "pass":"******"}'."\n");
方法四、在BusinessWorker里開一個內(nèi)部通訊端口
use GatewayWorker\Lib\Gateway;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
class Events
{
public static function onWorkerStart()
{
$http_worker = new Worker('http://0.0.0.0:8585');
$http_worker->reusePort = true;
$http_worker->onMessage = function (TcpConnection $connection, Request $request) {
$method = $request->get('method');
$args = $request->get('args');
if (method_exists(Gateway::class, $method)) {
call_user_func_array([Gateway::class, $method], $args);
return $connection->send('ok');
}
return $connection->send('fail');
};
$http_worker->listen();
}
// ... 其它業(yè)務(wù)邏輯省略 ...
}
這樣就可以通過http調(diào)用BusinessWorker進程,在進程內(nèi)部可以實現(xiàn)調(diào)用任意接口操作客戶端包括給客戶端推送數(shù)據(jù)。
例如內(nèi)部調(diào)用http://127.0.0.1:8585/?method=sendToAll&args[]=hello
時,會給所有客戶端發(fā)送hello
字符串
注意
以上代碼沒有做合法性驗證,如果調(diào)用方和BusinessWorker進程在同一臺服務(wù)器,
$http_worker = new Worker('http://0.0.0.0:8585');
可改為
$http_worker = new Worker('http://127.0.0.1:8585');
,
這樣只有本機才能調(diào)用8585端口,可省略合法性驗證。
如果是其它服務(wù)器調(diào)用8585端口,需要做請求合法性驗證。
如果其它服務(wù)器無法調(diào)用8585端口,請確認服務(wù)器防火墻、安全組、包括寶塔(如果有使用的話)開放了8585端口。