GatewayWorker与 Laravel 配合完成一个实时推送示例

1、推送结构示意图

2、准备框架及GatewayWorker

1)Laravel 基础框架下载

安装命令:composer create-project laravel/laravel example-app

在PHPStudy中配置网站,目录指向public

2)框架安装 GatewayClient 组件

在框架根目录执行命令:composer require workerman/gatewayclient

3)下载 GatewayWorker,解压到任意位置,进入GatewayWorker目录,双击start_for_win.bat启动【修改GatewayWorker代码需重启 start_for_win.bat】

4)设计思路【参考 GatewayWorker 文档】

Laravel 框架与GatewayWorker独立部署互不干扰

所有的业务逻辑都由Web页面请求到 Laravel 框架中完成

GatewayWorker 不接受客户端发来的数据,GatewayWorker仅作为单向的推送通道使用

仅当 Laravel 框架需要向浏览器主动推送数据时才在 Laravel 框架中调用 Gateway 的API GatewayClient 完成推送。

5)实现过程【参考 GatewayWorker 文档】

    1. Web页面建立与 GatewayWorker 的 websocket 连接

    2. GatewayWorker 发现有页面发起连接时,将对应连接的 client_id 发给Web页面

    3. Web页面收到client_id后触发一个ajax请求,将client_id发到框架后端

    4. 框架后端收到client_id后利用 GatewayClient 调用 Gateway::bindUid($client_id, $uid) 将client_id与当前uid(客户端唯一标识)绑定。

    5. 如果有群组、群发功能,也可以利用 Gateway::joinGroup($client_id, $group_id) 将client_id加入到对应分组

    6. 页面发起的所有请求都直接发送到 Laravel 框架统一处理,包括发送消息

    7. Laravel 框架处理业务过程中需要向某个uid或者某个群组发送数据时,直接调用 GatewayClient 的接口 Gateway::sendToUid、Gateway::sendToGroup 等发送即可

3、整理配置 GatewayWorker

1)配置 GatewayWorker\Applications\YourApp\start_gateway.php

代码如下:

<?php
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;

// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';

// gateway 进程,这里使用Text协议,可以用telnet测试
$gateway = new Gateway("websocket://0.0.0.0:8082");
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数
$gateway->count = 4;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = '127.0.0.1';
// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1237';
// 心跳间隔
$gateway->pingInterval = 30;
// 心跳检测
// 【0:代表服务端允许客户端不发送心跳,服务端不会因为客户端长时间没发送数据而断开连接】
// 【1,则代表客户端必须定时发送数据给服务端,否则pingNotResponseLimit*pingInterval秒内没有任何数据发来则关闭对应连接,并触发onClose】
$gateway->pingNotResponseLimit = 1;
// 服务端定时向客户端发送的数据
$gateway->pingData = '';

// 心跳间隔
//$gateway->pingInterval = 10;
// 心跳数据
//$gateway->pingData = '{"type":"ping"}';

/*
// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
$gateway->onConnect = function($connection)
{
    $connection->onWebSocketConnect = function($connection , $http_header)
    {
        // 可以在这里判断连接来源是否合法,不合法就关掉连接
        // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
        if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
        {
            $connection->close();
        }
        // onWebSocketConnect 里面$_GET $_SERVER是可用的
        // var_dump($_GET, $_SERVER);
    };
};
*/

// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
    Worker::runAll();
}

2)整理 GatewayWorker\Applications\YourApp\Events.php

<?php
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */

/**
 * 用于检测业务代码死循环或者长时间阻塞等问题
 * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
 * 然后观察一段时间workerman.log看是否有process_timeout异常
 */
//declare(ticks=1);

use \GatewayWorker\Lib\Gateway;

/**
 * 主逻辑
 * 主要是处理 onConnect onMessage onClose 三个方法
 * onConnect 和 onClose 如果不需要可以不用实现并删除
 */
class Events
{
    /**
     * 当客户端连接时触发
     * 如果业务不需此回调可以删除onConnect
     *
     * @param int $client_id 连接id
     */
    public static function onConnect($client_id)
    {
        // 向当前 client_id 发送数据
        Gateway::sendToClient($client_id,json_encode(['type' => 'init', 'client_id' => $client_id]));
    }

   /**
    * 当客户端发来消息时触发
    * @param int $client_id 连接id
    * @param mixed $message 具体消息
    */
   public static function onMessage($client_id, $message)
   {

   }

   /**
    * 当用户断开连接时触发
    * @param int $client_id 连接id
    */
   public static function onClose($client_id)
   {
       //调用用户下线处理方法
       //file_get_contents('http://www.la.cn/index/index/userClose/client_id/' . $client_id);
   }
}

4、框架核心代码

1)app/Http/Controllers/UserController.php

<?php

namespace App\Http\Controllers;

use GatewayClient\Gateway;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function __construct() {
        // Gateway 注册地址
        Gateway::$registerAddress = '127.0.0.1:1237';
    }

    /**
     * 消息推送页面
     */
    public function publish(){
        return view('user.publish');
    }

    /**
     * 订阅页面
     */
    public function subscribe()
    {
        return view('user.subscribe');
    }

    /**
     * 推送消息接口
     * @param Request $request 请求对象
     * @return string
     * @throws \Exception
     */
    public function publishSubmit(Request $request){
        $topic = $request->input('topic');
        $content = $request->input('content');
        $type = $request->input('type');
        if(empty($content) || empty($type)){
            return '{"status":"err","msg":"参数缺失"}';
        } else {
            $content = json_encode(['type'=>'msg','content'=>$content]);
        }

        //判斷为全局发送还是指定主题分组发送
        if(empty($topic)){
            //全局发送
            Gateway::sendToAll($content);
        } else {
            if($type === 'group'){
                //按主题推送
                Gateway::sendToGroup($topic,$content);
            } else {
                //按用户推送
                Gateway::sendToClient($topic,$content);
            }
        }
        return '{"status":"ok"}';
    }

    //绑定客户端连接和用户ID
    public function buildClient(Request $request){
        //信息
        $data = $request->input('data');
        if(!empty($data)){
            $data = json_decode($data, true);
        } else {
            return false;
        }
        if($data['type'] !== 'bind'){
            throw new \Exception("信息类型错误,client_ip:{$_SERVER['REMOTE_ADDR']}");
        }
        //执行绑定
        Gateway::bindUid($data['client_id'],$data['from_id']);
        return '{"status":"ok"}';
    }

    //订阅频道
    public function subscribeTopic(Request $request){
        //要订阅的频道
        $topic = $request->input('topic');
        if(empty($topic)){
            return '{"status":"err","msg":"请选择要订阅的频道"}';
        }
        //订阅人ID
        $client_id = $request->input('client_id');
        if(empty($client_id)){
            return '{"status":"err","msg":"用户信息有误"}';
        }

        $topic = json_decode($topic,true);
        //整理频道
        foreach($topic as $v){
            //执行订阅
            Gateway::joinGroup($client_id,$v['Topic']);
        }

        return '{"status":"ok"}';
    }

    //取消订阅频道
    public function unSubscribeTopic(Request $request){
        //要取消订阅的频道
        $topic = $request->input('topic');
        if(empty($topic)){
            return '{"status":"err","msg":"请选择要订阅的频道"}';
        }
        //订阅人ID
        $client_id = $request->input('client_id');
        if(empty($client_id)){
            return '{"status":"err","msg":"用户信息有误"}';
        }

        //执行取消订阅
        Gateway::leaveGroup($client_id,$topic);

        return '{"status":"ok"}';
    }

    //获取在线用户列表
    public function getOnlineUserList(){
        $list = Gateway::getAllClientIdList();
        return json_encode(['status'=>'ok','list'=>$list]);
    }

}

5、效果预览

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇