workerman + gateway +thinkphp 簡單使用


1.Workerman是什么?(套用官網)

Workerman是一款純PHP開發的開源高性能的PHP socket 服務框架。

Workerman不是重復造輪子,它不是一個MVC框架,而是一個更底層更通用的socket服務框架,你可以用它開發tcp代理、梯子代理、做游戲服務器、郵件服務器、ftp服務器、甚至開發一個php版本的redis、php版本的數據庫、php版本的nginx、php版本的php-fpm等等。Workerman可以說是PHP領域的一次創新,讓開發者徹底擺脫了PHP只能做WEB的束縛。

實際上Workerman類似一個PHP版本的nginx,核心也是多進程+Epoll+非阻塞IO。Workerman每個進程能維持上萬並發連接。由於本身常住內存,不依賴Apache、nginx、php-fpm這些容器,擁有超高的性能。同時支持TCP、UDP、UNIXSOCKET,支持長連接,支持Websocket、HTTP、WSS、HTTPS等通訊協以及各種自定義協議。擁有定時器、異步socket客戶端、異步Mysql、異步Redis、異步Http、異步消息隊列等眾多高性能組件。

 2. GatewayWorker是什么?(套用官網)

GatewayWorker基於Workerman開發的一個項目框架,用於快速開發TCP長連接應用,例如app推送服務端、即時IM服務端、游戲服務端、物聯網、智能家居等等

GatewayWorker使用經典的Gateway和Worker進程模型。Gateway進程負責維持客戶端連接,並轉發客戶端的數據給BusinessWorker進程處理,BusinessWorker進程負責處理實際的業務邏輯(默認調用Events.php處理業務),並將結果推送給對應的客戶端。Gateway服務和BusinessWorker服務可以分開部署在不同的服務器上,實現分布式集群。

 

3. Gatewayworker + thinkphp

數據交互模型:

流程:

  1. 客戶端(瀏覽器)發出socket請求與getwayworker建立連接
  2. 客戶端發出http請求(注意是發送http請求處理業務,所有業務都放在tp處理)
  3. tp處理業務邏輯(把client_id與uid綁定、客戶端分組、數據庫查詢等),然后調用gateway的接口把結果數據進行廣播
  4. 客戶端接收廣播的數據,進行視圖渲染

 

4. 環境搭建

  1. workerman 與 gateway安裝

composer require workerman/workerman
composer require workerman/gateway-worker

  官網實例 點擊下載 

  2. 配置

  start_gateway.php文件配置(開啟gateway服務,並在注冊服務注冊)

<?php 

use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 
$context = array(
    'ssl' => array(
        'local_cert'  => '/etc/pki/tls/certs/public.pem', // 或者crt文件
        'local_pk'    => '/etc/pki/tls/private/214498534070135.key',
        'verify_peer' => false
    )
);
// gateway 進程,這里使用websocket協議,這里使用了443端口,所有要加載ssl的配置,websocket://0.0.0.0:443:允許所有任何客戶端使用wss協議訪問
$gateway = new Gateway("websocket://0.0.0.0:443",$context);

$gateway->transport = 'ssl';
// gateway名稱,status方便查看
$gateway->name = 'YourAppGateway';
// gateway進程數
$gateway->count = 4;
// 本機ip,分布式部署時使用內網ip
$gateway->lanIp = '172.31.240.231';
// 內部通訊起始端口,假如$gateway->count=4,起始端口為4000
// 則一般會使用4000 4001 4002 4003 4個端口作為內部通訊端口 
$gateway->startPort = 2900;
// 服務注冊地址
$gateway->registerAddress = '172.31.240.231:1238';

// 心跳檢測  15秒一次
$gateway->pingInterval = 15;

$gateway->pingNotResponseLimit = 1;
// 當pingData為空,服務器將不會向客戶端發送心跳檢測(為了節省服務器資源,心跳檢測最好由客戶端發起) $gateway->pingData = '';
 if(!defined('GLOBAL_START')) {
   Worker::runAll();
 }

  3. start_businessworker.php(開啟businessworker服務,並在注冊服務注冊)

<?php 
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;

// bussinessWorker 進程
$worker = new BusinessWorker();
// worker名稱
$worker->name = 'YourAppBusinessWorker';
// bussinessWorker進程數量
$worker->count = 4;
// 服務注冊地址
$worker->registerAddress = '172.40.239.231:1238';

// 如果不是在根目錄啟動,則運行runAll方法
if(!defined('GLOBAL_START')) {
    Worker::runAll();
}

  4. start_register.php(開啟注冊服務)

<?php 
use \Workerman\Worker;
use \GatewayWorker\Register;

// register 服務必須是text協議
$register = new Register('text://172.40.239.231:1238');

// 如果不是在根目錄啟動,則運行runAll方法
if(!defined('GLOBAL_START')) {
    Worker::runAll();
}

   5.  啟動服務

    啟動

    以debug(調試)方式啟動

      php start.php start

    以daemon(守護進程)方式啟動

      php start.php start -d

    停止

      php start.php stop

    重啟

      php start.php restart

    平滑重啟

      php start.php reload

    查看狀態

      php start.php status

5. 客戶端操作(這是我基於小程序接口,封裝的一個socket庫)

    var socket = new Socket('wss://wss.xinyuruiyang.com');
    socket.on("open",function (res) {
      console.log('WebSocket連接已打開!')
      _this.setData({
        isOpenSocket: true
      });
    });
  // 初始化鏈接
  socket.on("init", function (data) {
    _this.setData({
      client_id: data.client_id
    });
    request({
      url: app.requestUrl('Admin/Match/bind'),
      data: {
        client_id: data.client_id,
        room: 1, // 分組1
      },
      success: res => {
        console.log(res, '用戶綁定成功!');
      }
    })
  })
 

6. gateway端操作

<?php
use \GatewayWorker\Lib\Gateway;
use \Workerman\Lib\Timer;
class Events
{
    public static $worker_id = null;
    /**
     * 當客戶端連接時觸發
     * 如果業務不需此回調可以刪除onConnect
     * 
     * @param int $client_id 連接id
     */
    public static function onConnect($client_id) {
        // 向當前client_id發送數據(觸發客戶端的init時間)
        Gateway::sendToClient($client_id, json_encode(["init",["client_id"=>$client_id]]));
    }
}

 

7. tp端操作  

  Gateway::$registerAddress = '172.31.239.230:1238';
   class MatchController extends AdminController {
private function bind_user($room,$client_id,$uid=''){
        if( empty($uid) ){
            $sk = $this->checksession();
            $openid = $this->get_openid($sk);
            $uid = D('User')->where(['openid'=>$openid])->getField('id');
        }

        // client_id與uid綁定
        Gateway::bindUid($client_id, $uid);
        // 加入某個群組(可調用多次加入多個群組)
        Gateway::joinGroup($client_id, $room);
     // 設置session Gateway
::setSession($client_id, ['uid'=>$uid]);
     // 獲取客戶端分組人數
$number = Gateway::getClientCountByGroup($room); D('Match')->where(['rid'=>$room,'uid'=>$uid,'status'=>['in',['1','2']]])->save(['client_id'=>$client_id]); $message = D('Match')->where(['rid'=>$room,'status'=>['in',['1','2']]])->select(); foreach($message as &$v){ $v['user'] = D('User')->where(['id'=>$v['uid']])->find(); $v['user']['teamside'] = D('Match')->where(['rid'=>$room,'uid'=>$v['uid'],'status'=>['in',['1','2']]])->getField('teamside'); }
     // 向某個房間廣播數據 Gateway
::sendToGroup($room, json_encode(["room",$message])); }
 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM