前言

之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛,就离不开通讯了,然后我就想到了长连接。这里本人用的是GatewayWorker框架。

什么是GatewayWorker框架?

GatewayWorker是基于Workerman开发的一套TCP长连接的应用框架,实现了单发、群发、广播等接口,内置了mysql类库,GatewayWorker分为Gateway进程和Worker进程,支持分布式部署,能够支持大量的连接数。

GatewayWorker的工作原理

1、启动所有进程(GatewayWorker、business、register)

2、GatewayWorker和business进程启动后向register请求注册

3、register服务收到注册请求后,把所有Gateway的通讯地址保存在内存中同时把内存中所有的Gateway的通讯地址发给business

4、business进程得到所有的Gateway内部通讯地址后进行连接GatewayWorker

5、如果有新的GatewayWorker服务进行register,则将新的Gateway内部通讯地址列表将广播给所有buiness并建立连接

6、如果有GatewayWorker下线,则Register服务会收到通知,会将该GatewayWorker内部通讯地址删除,然后广播新的内部通讯地址列表给所有business

7、此时GatewayWorker与buiness已经建立起长连接

8、客户端的事件及接受的数据全部由GatewayWorker转发给business进行处理。

目录结构

├── Applications // 项目应用目录
│ └── YourAppGateway // 建立一个存放workman的目录,名字随意
│ ├── Events.php // 处理主逻辑业务的文件,管理onConnect onMessage onClose 等方法
│ ├── start_gateway.php // gateway进程启动脚本、配置服务注册地址、端口号、进程数等参数
│ ├── start_businessworker.php // 用户进程的启动脚本
│ └── start_register.php // 注册服务的启动脚本

├── start.php // 全局启动脚本,此脚本会依次加载Applications/YourAppGateway/start*.php对所有脚本进行启动

└── vendor // GatewayWorker框架和Workerman框架源码目录

GatewayWorker实现

以宝塔为例

1.安装composer

登录SSH终端,使用以下命令下载Composer的安装脚本:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"

运行下面的命令来安装Composer:

php composer-setup.php --install-dir=/usr/local/bin --filename=composer

检查composer版本

composer -v //检查composer版本

2.安装workerman

在项目根目录打开宝塔终端,输入以下命令安装workman

composer require topthink/think-worker

3.安装GatewayWorker

在项目根目录打开宝塔终端,输入以下命令安装GatewayWorker

composer require workerman/gateway-worker

4.实现代码

可以选择官方提供的demo 链接:http://www.workerman.net/download/GatewayWorker.zip

或者使用我根据demo改编而来的

先在项目应用目录(一般是Applications)下新建一个文件存储以下四个进程文件

start_gateway.php

<?php
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:8283");
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数
$gateway->count = 200;
// 本机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 = 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();
}

start_businessworker.php

<?php
use Workerman\Worker;
use Workerman\WebServer;
use GatewayWorker\Gateway;
use GatewayWorker\BusinessWorker;
use Workerman\Autoloader; // 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php'; // bussinessWorker 进程
$worker = new BusinessWorker();
// worker名称
$worker->name = 'YourAppBusinessWorker';
// bussinessWorker进程数量
$worker->count = 200;
// 服务注册地址、端口
$worker->registerAddress = '127.0.0.1:1237'; // 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}

start_register.php

<?php
use \Workerman\Worker;
use \GatewayWorker\Register; // 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php'; // register 必须是text协议 1237为端口
$register = new Register('text://0.0.0.0:1237'); // 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}

Events.php

<?php

use \GatewayWorker\Lib\Gateway;

/**
* 主逻辑
* 主要是处理 onConnect onMessage onClose 三个方法
*/
class Events
{
/**
* 当客户端连接时触发
*
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
echo "【新的客户端链接】:client_id:".$client_id.PHP_EOL;
// 向当前client_id发送数据
Gateway::sendToClient($client_id, ""); // 向所有人发送
$data=[
'client_id'=>$client_id,
'message'=>'欢迎'.$client_id.'登录!',
'data'=>[]
];
Gateway::sendToAll(json_encode($data));
// Gateway::sendToAll("$client_id login\r\n");
} /**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $message){ $data=[
'client_id'=>$client_id,
'message'=>$client_id.'说:'.$result['message'],
'data'=>$message
];
Gateway::sendToAll(json_encode($data));
// 向所有人发送
// Gateway::sendToAll("$client_id said $message\r\n");
} /**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose($client_id)
{
// 向所有人发送
// GateWay::sendToAll("$client_id 退出了!\r\n");
}
}

随后在项目的根目录下新建一个启动文件

start_all_workman.php

<?php

ini_set('display_errors', 'on');
use Workerman\Worker; if(strpos(strtolower(PHP_OS), 'win') === 0)
{
exit("start.php not support windows, please use start_for_win.bat\n");
} // 检查扩展
if(!extension_loaded('pcntl'))
{
exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
} if(!extension_loaded('posix'))
{
exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
} // 标记是全局启动
define('GLOBAL_START', 1); require_once __DIR__ . '/vendor/autoload.php'; // 加载所有Applications/*/start.php,以便启动所有服务
foreach(glob(__DIR__.'/application/此处请改成你自己命名存放workman的目录名/start*.php') as $start_file)
{
require_once $start_file;
}
// 运行所有服务
Worker::runAll();

注意开启端口后记得去放行端口!!!除了宝塔放行以外,你的服务器(阿里云/腾讯云等等)也记得要去放行!!!

启动workman

在项目根目录下打开终端,输入php start_all_workman.php start -d ,开启守护进程,如果出现一下页面即成功开启

如想关闭workman进程则输入php start_all_workman.php stop 进行关闭

GatewayWorker使用

如果你的网站使用的是Https协议的话,WebSocket必须使用wss协议

但是wss协议不支持IP:端口的形式,而是只能写域名+url

所以为了解决使用https协议而WebSocket不能连接的问题,可以使用Nginx进行反向代理

在网站配置文件的server下加入以下代码

location /connectWorkman(名字随你取,别跟其他反向代理重名就行)
{
proxy_pass http://127.0.0.1:8283;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Real-IP $remote_addr;
}

前端使用(uniapp)

init() {
SocketTask = uni.connectSocket({
url: 'wss://chat.gdpaimaihui.com/auction', //正式
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log('WebSocket连接创建', res);
},
fail: function(err) {
uni.showToast({
title: '网络异常!',
icon: 'none'
});
console.log(err);
}
}); //websocket监听事件
SocketTask.onOpen((res) => {
socketOpen = true
canReconnect = true
console.log('监听 WebSocket 连接打开事件。', res);
//websocket连接后可以启动个定时器,每隔一段时间进行心跳一次,以防心跳停止断开连接
this.timer = setInterval(() => {
SocketTask.send({
data: '心跳',
success() {
// console.log('发送心跳成功');
}
})
}, 2000)
});
SocketTask.onError((onError) => {
console.log('监听 WebSocket 错误。错误信息', onError);
socketOpen = false;
if (canReconnect) {
this.reconnect()
canReconnect = false
}
}); SocketTask.onMessage((res) => {
console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息', res);
});
}, //重新连接
reconnect() {
if (!socketOpen) {
let count = 0;
reconnectInterval = setInterval(() => {
console.log("正在尝试重连")
uni.showToast({
title: '正在尝试重连',
icon: 'none'
})
this.init();
count++
console.log();
//重连一定次数后就不再重连
if (count >= reconnectTimes) {
clearInterval(reconnectInterval)
uni.showToast({
title: '网络异常或服务器错误',
icon: 'none'
})
}
}, reconnectDelay)
}
}

上述为之前给公司做内部通讯软件时个人整理内容,水平有限,如有错误之处,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!谢谢~๑•́₃•̀๑ [鲜花][鲜花][鲜花]

【workerman】uniapp+thinkPHP5使用GatewayWorker实现实时通讯的更多相关文章

  1. Uniapp使用GoEasy实现websocket实时通讯

    Uniapp作为近来最火的移动端开发技术,一套代码,可以打包成Android/iOS app和各种平台的小程序,可谓是没有最方便只有更方便. GoEasy上架DCloud Uniapp插件市场已经有一 ...

  2. 使用Websocket框架之GatewayWorker开发电商平台买家与卖家实时通讯

    前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交易,要开发此功能当时首先考虑到的就是swoole和workerman了,从网上大概了解了一下关于这两款 ...

  3. 基于TP5使用Websocket框架之GatewayWorker开发电商平台买家与卖家实时通讯

    https://www.cnblogs.com/wt645631686/p/7366924.html 前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交 ...

  4. PHP基于TP5使用Websocket框架之GatewayWorker开发电商平台买家与卖家实时通讯

    前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交易,要开发此功能当时首先考虑到的就是swoole和workerman了,从网上大概了解了一下关于这两款 ...

  5. 微信小程序实时通讯(websocket)问题

    这几天值班忙的不要不要,人工智能这块看的都是零零散散,今天就来写写小程序的实时通讯吧.小程序端://这个是连接 lianjie:function(){ var socketOpen = false / ...

  6. Android IOS WebRTC 音视频开发总结(六二)-- 大数据解密国外实时通讯行业开发现状

    本文主要介绍国外实时通讯行业现状,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号blackerteam,更多详见www.blackerteam.com 上篇文章我们采用百度搜索指数来分 ...

  7. Android IOS WebRTC 音视频开发总结(六一)-- 大数据解密国内实时通讯行业开发现状

    本文主要介绍国内实时通讯行业现状,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号blackerteam,更多详见www.blackerteam.com 这几年移动互联网发展势头很猛,与 ...

  8. [渣译文] SignalR 2.0 系列:SignalR的高频实时通讯

    原文:[渣译文] SignalR 2.0 系列:SignalR的高频实时通讯 英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.N ...

  9. B/S(Web)实时通讯解决方案

    B/S的实时通讯实现起来比较麻烦,因为http协议是无状态的,导致一些实时消息通知和聊天等功能比较难以实现,本文主要简述几种自己之前常用的几种方式. 1.传统的HTTP协议是无状态的 传统的HTTP协 ...

  10. 仿PC版微信的练手项目(可实时通讯)

    仿PC版微信的DEMO 本项目是由一个仿PC版微信的vue前端项目,和一个使用leancloud进行数据存储的.提供WebSocket的node后端项目构成. 本项目使用的技术栈:vue + vue- ...

随机推荐

  1. WEB服务与NGINX(1)-HTTP协议基础

    WEB服务与NGINX(1) 目录 WEB服务与NGINX(1) 1. HTTP协议 1.1 WEB资源 1.2 URI简介 1.3 WEB服务请求处理过程 1.4 HTTP报文结构 1.4.1 re ...

  2. Spring源码阅读 ------------------- SpringFrameWork 5.2 术语理解(三)

    一.一定要理解的概念 1.控制反转 对象A和对象B,对象A中需要new 一个对象B,但是,现在需要对象A,不在自己内部new 对象B,把new 对象B的权限交给第三方(IOC框架),操作的过程,就是控 ...

  3. Tkinter禁止用户调整窗口尺寸大小

    禁止用户调整窗口尺寸大小的方式: root.resizable(False,False) 例子: from tkinter import * from tkinter import ttk impor ...

  4. 一分钟部署prometheus&grafana全方面监控SpringBoot项目

    0x01 创建目录 找一个你喜欢的地方,创建项目根目录 example: [root@demo-78 ~]# mkdir /data/prometheus 0x02 创建配置文件 进入到项目根目录: ...

  5. 🐞vue兄弟组件中方法互相调用

    场景:父组件中同时引入两个子组件(A和B),此时B组件点击按钮需要调用A组件里面的方法 方案1:vue的事件总线 方案2:自定义事件($emit) 最终方案:方案2 父组件 具体操作 B组件上添加一个 ...

  6. js 留言板(带删除功能)

    本文所用的知识点:创建节点和添加节点 创建节点:document.createElement('li') 添加节点  node(父亲节点).appendChild(child)    child:子节 ...

  7. Vue.js 动画与过渡效果实战

    title: Vue.js 动画与过渡效果实战 date: 2024/6/4 updated: 2024/6/4 description: 这篇文章介绍了如何在网页设计中使用过渡动画和组件效果,以及如 ...

  8. C# .NET HttpWebRequest 按每个(单个)请求跳过证书校验

    C# .NET HttpWebRequest 按每个(单个)请求跳过证书校验 自签名证书 HTTPS TLS . 使用.NET 4.5 新加的属性 HttpWebRequest.ServerCerti ...

  9. redis安全篇

    redis被攻击,作为突破口,服务器惨遭毒手的事太常见了. 大多数云服务器被攻击,都是redis,mongodb等数据库被入侵. 因此修改端口,密码,以及注意bind运行地址,是必须. 思考是否要暴露 ...

  10. xxlJob端口号及故障转移设置,解决负载均衡调度任务执行

    xxlJob端口号及故障转移设置,解决负载均衡调度任务执行 my.xxljob.executorPort = 1162 my.xxljob.executorAppName = myService-jo ...