workerman笔记-1

多线程用Channel通讯

  1. 开启Channel服务
new \Channel\Server('127.0.0.1',2206); //默认127.0.0.1 prot:2206
  1. 在onWorkerStart()中链接Channel服务并注册事件
//链接服务
\Channel\Client::connect('127.0.0.1',2206);
//注册响应事件
$event_name = '事件名称';
\Channel\Client::on($event_name,function($event_data){
	//处理逻辑
});
//调用事件 - 此操作会触发所有子进程中的$event_name事件
\Channel\Client::publish($event_name,$data);
## workerman多线程推送消息
<?php

namespace app\http\controller;

use think\worker\Server;
use \Workerman\Lib\Timer;
use app\common\library\Auth;
use app\common\library\Token;
use app\common\model\Message;
use \Channel\Client;

class Worker extends Server
{
    protected $socket = 'websocket://0.0.0.0:9503';
    protected $uidConnections = [];
    protected $uidConnectionIds = [];
    protected $HEARTBEAT_TIME = '300';
    protected $auth;
    protected $redis;
    protected $config;
    protected $worker;

    protected $option = [
        'name'      => 'worker name',     //进程名称,方便识别
        'count'     => 4,
        'transport' => 'ssl'
    ];

    // protected $context = [
    //     'ssl' => array(
    //         // 请使用绝对路径
    //         'local_cert'        => ROOT_PATH . '/pem/server.pem', // 也可以是crt文件
    //         'local_pk'          => ROOT_PATH . '/pem/server.key',
    //         'verify_peer'       => false,
    //         'allow_self_signed' => false, //如果是自签名证书需要开启此选项
    //     )
    // ];

    /**
     * 发送消息加入Redis列表
     *
     * @param [type] $connection
     * @param [type] $data
     * @return void
     */
    public function onMessage($connection, $data)
    {
        try {
            $data = json_decode($data, true);
            $type = @$data['type'];
            //需要验签的通讯
            if ($type != 'admin') { //后台获取实时订单
                if (!isset($data['socket_id']) || empty($data['socket_id'])) { //无socket_id 直接关闭通讯释放资源
                    $connection->close($this->error());
                } else {
                    //效验token是否有效
                    if ($this->auth->init($data['socket_id']) == false) { //socket_id 无效 释放资源
                        $connection->close($this->error());
                    }
                    $uid = $this->redis->get($data['socket_id']);
                    if (empty($uid)) { //无实际用户 释放资源
                        $connection->close($this->error());
                    }
                    if (isset($this->uidConnections[$uid])) { // 单点登录措施, 顶出上一链接;
                        $a = $this->uidConnections[$uid];
                        if ($a->id != $connection->id) {
                            $a->close($this->error('您已在其他地方登录!'));
                        }
                    }
                }
                switch ($type) {
                    case 'register': //注册链接
                        $uidConnections = $this->uidConnections;
                        $uidConnections[$uid] = $connection;
                        $this->uidConnections = $uidConnections;
                        $uidConnectionIds = $this->uidConnectionIds;
                        $uidConnectionIds[$connection->id] = $uid;
                        $this->uidConnectionIds = $uidConnectionIds;
                        $this->redis->zAdd($this->config['prefix'] . 'socketOnline', true, $uid);
                        $connection->send($this->success(['message' => '通讯成功'], '通讯成功!'));
                        //执行发送历史消息推送服务
                        $this->history($uid);
                        break;
                    case 'ping':
                        $connection->send($this->pong($uid)); //心跳
                        break;
                    default:
                        $connection->send($this->error()); //返回错误
                        break;
                }
            }
        } catch (\Exception $e) {
            echo $e->getMessage() . "\r\n";
        }
    }

    public function onWorkerStart($worker)
    {
        //需要跨线程做的事都写在这里
        // Channel客户端连接到Channel服务端
        \Channel\Client::connect('127.0.0.1', 2206);
        // 注册"Findandforward"事件,用于消息转发时当前进程找不到对应连接时调用
        $_this = $this;
        \Channel\Client::on('Findandforward', function ($event_data) use ($worker, $_this) {
            $type = $event_data['type'];
            switch ($type) {
                case 'Message':
                    $messageModel = new \app\common\model\Message();
                    if (isset($event_data['uid']) && $event_data['uid'] > 0) {
                        echo "给指定会员推送:" . $event_data['uid'] . "\r\n";
                        if (isset($_this->uidConnections) && is_array($_this->uidConnections)) {
                            if (isset($_this->uidConnections[$event_data['uid']])) {
                                echo $event_data['uid'] . "\r\n";
                                $result = $messageModel->getNoReadMessageNum2($event_data['uid']);
                                $_this->uidConnections[$event_data['uid']]->send($_this->success($result, 'newMessage'));
                            }
                        }
                    } else {
                        echo '给所有人广播' . "\r\n";
                        if (isset($_this->uidConnections) && is_array($_this->uidConnections)) {
                            foreach ($_this->uidConnections as $k => $v) {
                                $result = $messageModel->getNoReadMessageNum2($k);
                                var_dump($k);
                                $v->send($_this->success($result, 'newMessage'));
                            }
                        }
                    }
                    break;
            }
        });
        // 注册"Findandclose"事件,用于连接断开时当前进程找不到对应连接
        \Channel\Client::on('Findandclose', function ($event_data) use ($worker, $_this) {
            $connectionId = $event_data['connecttion_id'];
            if (isset($_this->uidConnectionIds[$connectionId])) {
                $uid = $_this->uidConnectionIds[$connectionId];
                unset($_this->uidConnectionIds[$connectionId]);
                unset($_this->uidConnections[$uid]);
            }
        });
        try {
            $this->auth = Auth::instance();
            $config = getRedis(1);
            $redis = $config['redis'];
            $this->config = $config;
            $this->redis = $redis;
            // 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
            $inner_text_worker = new \Workerman\Worker('Text://127.0.0.1:7755');
            $inner_text_worker->onMessage = function ($connection, $buffer) {
                $buffer = json_decode($buffer, true);
                switch ($buffer['type']) {
                        //消息改变推送数量
                    case 'newMessage':
                        $uid = $buffer['uid'];
                        \Channel\Client::publish('Findandforward', ['type' => 'Message', 'uid' => $uid]);
                        break;
                    case 'systemMessageToChange':
                        \Channel\Client::publish('Findandforward', ['type' => 'Message']);
                        break;
                    default:
                        $connection->send(false);
                        break;
                }
                $connection->send('success');
            };
            $inner_text_worker->listen();
        } catch (\Exception $e) {
            echo $e->getMessage() . "\r\n";
        }
    }
    /**
     * 关闭链接释放资源
     *
     * @param [type] $connection
     * @return void
     */
    public function onClose($connection)
    {
        try {
            $id = $connection->id;
            if (isset($this->uidConnectionIds[$id])) {
                $uid = $this->uidConnectionIds[$id];
                unset($this->uidConnectionIds[$id]);
                unset($this->uidConnections[$uid]);
                $this->redis->zRem($this->config['prefix'] . 'socketOnline', $uid);
            } else {
                \Channel\Client::publish('Findandclose', ['connecttion_id' => $id]);
            }
        } catch (\Exception $e) {
        }
    }

    public function history($uid)
    {
        $connection = $this->uidConnections[$uid];
        if ($connection) {
            $userModel = new \app\common\model\User();
            $user = $userModel->get($uid);
            if ($user) {
                $messageModel = new \app\common\model\Message();
                $result = $messageModel->getNoReadMessageNum($user['id'], $user['is_redmom']);
                $this->sendMessageByUid($result, $uid, 'newMessage');
            }
        }
    }
    /**
     * 给指定用户发送消息
     *
     * @param array $data
     * @param string $uid
     * @return void
     */
    public function sendMessageByUid($data = [], $uid = '', $type = 'success', $uidConnections = [])
    {
        $uidConnections = $this->uidConnections;
        if (isset($uidConnections[$uid]) && !empty($uidConnections[$uid])) { //如果当前用户已建立链接 则推送后修改消息
            $uidConnections[$uid]->send($this->success($data, $type));
        }
    }

    /**
     * 推送消息
     *
     * @param array $data
     * @param string $uid
     * @return void
     */
    public function send($data = [], $uid = '')
    {
        foreach ($this->uidConnections as $k => $v) {
            if (!empty($uid) && $uid == $k) {
                $v->send($this->success($data));
            } else if (empty($uid)) {
                $v->send($this->success($data));
            }
        }
    }
    /**
     *  返回错误信息
     */
    public function error($msg = '通讯异常, 参数错误!')
    {
        return json_encode(array('msg' => $msg, 'type' => 'error', 'time' => date('Y-m-d H:i:s')), JSON_UNESCAPED_UNICODE);
    }

    /**
     * 返回正确的信息
     *
     * @param array $data
     * @param string $msg
     * @param string $datatype
     * @return void
     */
    public function success($data = [], $type = "success", $msg = 'success')
    {
        return json_encode(array('data' => $data, 'msg' => $msg, 'type' => $type,  'time' => date('Y-m-d H:i:s')), JSON_UNESCAPED_UNICODE);
    }

    /**
     * 返回心跳反应
     *
     * @return void
     */
    public function pong($data = [])
    {
        return json_encode(array('data' => $data, 'msg' => '心跳', 'type' => 'pong', 'online' => count($this->uidConnections), 'time' => date('Y-m-d H:i:s')), JSON_UNESCAPED_UNICODE);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值