一个基于ReactPHP的分布式WebSocket中心管理系统,支持WebSocket服务实例注册到多个注册中心,并支持WebSocket服务实例通过任一注册中心进行服务调用。
- 🚀 基于ReactPHP的高性能异步WebSocket服务
- 🔄 分布式WebSocket服务注册中心
- 💬 支持多服务实例的消息广播和路由
- 🌐 RESTful API接口支持
- 📱 Web前端管理界面
- 🔧 灵活的中间件支持
使用Composer安装:
composer require reactphp-x/websocket-center
每个注册中心实例都包含注册服务和对应的HTTP API服务。可以启动多个注册中心实例形成集群。
# 启动第一个注册中心
REGISTER_CENTER_PORT=8010 HTTP_PORT=8011 OTHER_REGISTER_CENTER_PORT=8012,8014 php examples/register.php
# 启动第二个注册中心
REGISTER_CENTER_PORT=8012 HTTP_PORT=8013 php examples/register.php
# 启动第三个注册中心
REGISTER_CENTER_PORT=8014 HTTP_PORT=8015 php examples/register.php
环境变量说明:
REGISTER_CENTER_PORT
: 注册中心服务端口(建议使用偶数端口:8010, 8012, 8014...)HTTP_PORT
: 对应的HTTP API服务端口(建议使用奇数端口:8011, 8013, 8015...)
可以启动多个WebSocket服务实例:
# 启动第一个WebSocket服务实例
REGISTER_CENTER_PORT=8010 PORT=8090 php examples/websocket.php
# 启动第二个WebSocket服务实例
REGISTER_CENTER_PORT=8010 PORT=8091 php examples/websocket.php
环境变量说明:
REGISTER_CENTER_PORT
: 注册中心端口(需与注册中心保持一致)PORT
: WebSocket服务端口TOKEN
: 可选的访问令牌,多个令牌用逗号分隔
php -S 0.0.0.0:9013 examples/index.html
然后访问 http://localhost:9013 查看演示界面。
前端界面支持两种连接方式:
- HTTP API连接 - 通过注册中心的HTTP API端口(如8011, 8013等)进行服务管理
- WebSocket连接 - 直接连接到WebSocket服务端口(如8090, 8091等)进行实时通信
┌─────────────────┐
│ Web Frontend │
│ (index.html) │
└─────────┬───────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│HTTP │ │HTTP │ │HTTP │
│API 1 │ ... │API 2 │ ... │API N │
│(8011) │ │(8013) │ │(801Y) │
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│Reg. │ │Reg. │ │Reg. │
│Ctr 1 │ │Ctr 2 │ ... │Ctr N │
│(8010) │ │(8012) │ │(801X) │
└───────┘ └───────┘ └───────┘
│ │ │
└─────────────────────┼─────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│WS │◄───────────►│WS │◄───────────►│WS │
│Svc 1 │ │Svc 2 │ ... │Svc N │
│(8090) │ │(8091) │ │(809X) │
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
└─────────────────────┼─────────────────────┘
│
┌─────────▼───────┐
│ Web Frontend │
│ WebSocket连接 │
│ (直连WS服务) │
└─────────────────┘
架构特点:
- 双重连接模式 - Web Frontend既可以通过HTTP API调用WebSocket服务,也可以直接连接WebSocket服务端口进行实时通信
- HTTP API与注册中心一对一 - 每个注册中心实例都有对应的HTTP API服务,提供独立的管理接口
- 多注册中心集群 - 可以部署多个注册中心实例,每个运行在不同端口,形成分布式集群
- 跨注册中心连接 - 每个WebSocket服务可以同时连接到多个注册中心,实现多点注册
- 服务调用 - 通过多个注册中心实现WebSocket服务集群的调用
- 负载均衡 - 多个WebSocket服务实例自动分担客户端连接负载,可以通过用户ID取余连接到对应Websocket服务实例
- 高可用性 - 单个注册中心或HTTP API故障不影响其他节点的正常运行
API支持GET和POST请求,但token
参数始终通过查询字符串传递。事件名称通过event
参数指定,其他参数可以通过查询字符串(GET)或请求体(POST)传递。
GET请求:
GET http://localhost:8011/?event={eventName}&token={your_token}&{paramName}[{subKey}]={value}
POST请求:
POST http://localhost:8011/?event={eventName}&token={your_token}
Content-Type: application/json
{
"paramName": {
"subKey": "value"
}
}
所有API调用返回统一的JSON格式:
{
"code": 0,
"msg": "ok",
"extra": [],
"master_ids": [
"98b95842c9e26e8fb113cf8342526f5d",
"de5d37e88761c59d61bd6b38822de96f"
],
"data": {
"eventName": {
"98b95842c9e26e8fb113cf8342526f5d": "节点1的返回数据",
"de5d37e88761c59d61bd6b38822de96f": "节点2的返回数据"
}
}
}
字段说明:
code
: 状态码,0表示成功,非0表示失败msg
: 状态消息extra
: 额外信息数组master_ids
: 当前在线的WebSocket服务节点ID列表data
: 各个节点的返回数据,key为节点ID,value为该节点的执行结果
GET http://localhost:8011/?event=getConnectionCount&token=your_token
GET http://localhost:8011/?event=getIds&token=your_token
GET http://localhost:8011/?event=get_Ids&token=your_token
GET http://localhost:8011/?event=isOnlineId&token=your_token&isOnlineId[id]=user123
GET http://localhost:8011/?event=isOnline_Id&token=your_token&isOnline_Id[_id]=connection123
GET请求:
GET http://localhost:8011/?event=bindId&token=your_token&bindId[id]=user123&bindId[_id]=connection123
POST请求:
POST http://localhost:8011/?event=bindId&token=your_token
Content-Type: application/json
{
"bindId": {
"id": "user123",
"_id": "connection123"
}
}
返回示例:
{
"code": 0,
"msg": "ok",
"extra": [],
"master_ids": ["98b95842c9e26e8fb113cf8342526f5d"],
"data": {
"bindId": {
"98b95842c9e26e8fb113cf8342526f5d": 1
}
}
}
GET http://localhost:8011/?event=unBindId&token=your_token&unBindId[id]=user123
GET http://localhost:8011/?event=unBind_Id&token=your_token&unBind_Id[_id]=connection123
GET http://localhost:8011/?event=getGroupIds&token=your_token
GET http://localhost:8011/?event=joinGroupById&token=your_token&joinGroupById[groupId]=room1&joinGroupById[id]=user123
GET http://localhost:8011/?event=joinGroupBy_Id&token=your_token&joinGroupBy_Id[groupId]=room1&joinGroupBy_Id[_id]=connection123
GET http://localhost:8011/?event=leaveGroupById&token=your_token&leaveGroupById[groupId]=room1&leaveGroupById[id]=user123
GET http://localhost:8011/?event=leaveGroupBy_Id&token=your_token&leaveGroupBy_Id[groupId]=room1&leaveGroupBy_Id[_id]=connection123
GET http://localhost:8011/?event=leaveAllGroupById&token=your_token&leaveAllGroupById[id]=user123
GET http://localhost:8011/?event=leaveAllGroupBy_Id&token=your_token&leaveAllGroupBy_Id[_id]=connection123
GET http://localhost:8011/?event=getGroupIdsById&token=your_token&getGroupIdsById[id]=user123
GET http://localhost:8011/?event=getGroupIdsBy_Id&token=your_token&getGroupIdsBy_Id[_id]=connection123
GET http://localhost:8011/?event=isInGroupById&token=your_token&isInGroupById[groupId]=room1&isInGroupById[id]=user123
GET http://localhost:8011/?event=isInGroupBy_Id&token=your_token&isInGroupBy_Id[groupId]=room1&isInGroupBy_Id[_id]=connection123
GET http://localhost:8011/?event=getGroupIdCount&token=your_token&getGroupIdCount[groupId]=room1
GET http://localhost:8011/?event=getGroup_IdCount&token=your_token&getGroup_IdCount[groupId]=room1
GET请求:
GET http://localhost:8011/?event=sendMessageToId&token=your_token&sendMessageToId[id]=user123&sendMessageToId[msg]=Hello%20World
POST请求:
POST http://localhost:8011/?event=sendMessageToId&token=your_token
Content-Type: application/json
{
"sendMessageToId": {
"id": "user123",
"msg": "Hello World"
}
}
返回示例:
{
"code": 0,
"msg": "ok",
"extra": [],
"master_ids": ["98b95842c9e26e8fb113cf8342526f5d", "de5d37e88761c59d61bd6b38822de96f"],
"data": {
"sendMessageToId": {
"98b95842c9e26e8fb113cf8342526f5d": 1,
"de5d37e88761c59d61bd6b38822de96f": 0
}
}
}
注:返回值表示每个节点成功发送的消息状态
GET http://localhost:8011/?event=sendMessageTo_Id&token=your_token&sendMessageTo_Id[_id]=connection123&sendMessageTo_Id[msg]=Hello%20World
GET http://localhost:8011/?event=sendToGroup&token=your_token&sendToGroup[groupId]=room1&sendToGroup[msg]=Hello%20Group
GET请求:
GET http://localhost:8011/?event=broadcast&token=your_token&broadcast[msg]=Hello%20Everyone
POST请求:
POST http://localhost:8011/?event=broadcast&token=your_token
Content-Type: application/json
{
"broadcast": {
"msg": "Hello Everyone"
}
}
返回示例:
{
"code": 0,
"msg": "ok",
"extra": [],
"master_ids": ["98b95842c9e26e8fb113cf8342526f5d", "de5d37e88761c59d61bd6b38822de96f"],
"data": {
"broadcast": {
"98b95842c9e26e8fb113cf8342526f5d": 0,
"de5d37e88761c59d61bd6b38822de96f": 0.
}
}
}
注:返回值表示每个节点广播到的连接数量
GET http://localhost:8011/?event=sendMessageToGroupByOnlyId&token=your_token&sendMessageToGroupByOnlyId[id]=user123&sendMessageToGroupByOnlyId[msg]=Hello%20Groups
GET http://localhost:8011/?event=sendMessageToGroupByOnly_Id&token=your_token&sendMessageToGroupByOnly_Id[_id]=connection123&sendMessageToGroupByOnly_Id[msg]=Hello%20Groups
可以在单个请求中执行多个操作,用逗号分隔事件名称:
GET请求:
GET http://localhost:8011/?event=getConnectionCount,getIds&token=your_token
POST请求:
POST http://localhost:8011/?event=getConnectionCount,getIds&token=your_token
Content-Type: application/json
{
"getConnectionCount": {},
"getIds": {}
}
返回示例:
{
"code": 0,
"msg": "ok",
"extra": [],
"master_ids": ["98b95842c9e26e8fb113cf8342526f5d", "de5d37e88761c59d61bd6b38822de96f"],
"data": {
"getConnectionCount": {
"98b95842c9e26e8fb113cf8342526f5d": 5,
"de5d37e88761c59d61bd6b38822de96f": 3
},
"getIds": {
"98b95842c9e26e8fb113cf8342526f5d": ["user1", "user2"],
"de5d37e88761c59d61bd6b38822de96f": ["user3"]
}
}
}
当请求失败时,返回结构如下:
{
"code": 0,
"msg": "ok",
"extra": [],
"master_ids": ["98b95842c9e26e8fb113cf8342526f5d"],
"data": {
"getIds": {
"98b95842c9e26e8fb113cf8342526f5d": [
"code": 1,
"msg": "xxx",
"data": []
],
}
}
}
常见错误码:
1
: token错误或事件参数错误- 其他错误码根据具体业务逻辑定义
<?php
require __DIR__ . '/../vendor/autoload.php';
use ReactphpX\WebsocketGroup\WebsocketGroupComponent;
use ReactphpX\WebsocketGroup\WebsocketGroupMiddleware;
use ReactphpX\ConnectionGroup\ConnectionGroup;
use ReactphpX\RegisterCenter\Master;
use ReactphpX\RegisterCenter\ServiceRegistry;
// 创建连接组
$connectionGroup = new ConnectionGroup;
// 注册服务到注册中心
ServiceRegistry::register('websocket', $connectionGroup, [
'env' => 'prod',
]);
// 连接到注册中心
$master = new Master(logger: $logger);
$master->connectViaConnector('127.0.0.1', getenv('REGISTER_CENTER_PORT') ?: 8010);
// 启动WebSocket服务
$websocketGroupMiddleware = new WebsocketGroupMiddleware($connectionGroup);
$http = new React\Http\HttpServer(
$websocketGroupMiddleware,
new WebsocketMiddleware(new WebsocketGroupComponent($connectionGroup))
);
$socket = new React\Socket\SocketServer('0.0.0.0:' . getenv('PORT') ?: 8090);
$http->listen($socket);
<?php
require __DIR__ . '/../vendor/autoload.php';
use ReactphpX\RegisterCenter\Register;
use ReactphpX\WebsocketCenter\RegisterMiddleware;
// 创建注册中心
$center = new Register(getenv('REGISTER_CENTER_PORT') ?: 8010, $loop, $logger);
$center->start();
// 创建HTTP API服务
$http = new React\Http\HttpServer(new RegisterMiddleware($center));
$socket = new React\Socket\SocketServer('0.0.0.0:' . getenv('HTTP_PORT') ?: 8011);
$http->listen($socket);
$connectionGroup->on('open', function ($conn, $request) use ($connectionGroup) {
// 新连接建立时触发
$connectionGroup->sendMessageTo_id($conn->_id, json_encode([
'cmd' => 'open',
'_id' => $conn->_id,
]));
});
$connectionGroup->on('message', function ($from, $msg) use ($connectionGroup) {
// 收到消息时触发
if ($msg == 'ping') {
$connectionGroup->sendMessageTo_id($from->_id, json_encode([
'cmd' => 'pong',
'_id' => $from->_id,
]));
}
});
$connectionGroup->on('close', function ($conn, $reason) {
// 连接断开时触发
echo "Connection {$conn->_id} closed: {$reason}\n";
});
演示界面提供以下功能:
-
WebSocket连接管理
- 连接到WebSocket服务
- 实时显示连接状态
- 自动重连机制
-
消息发送
- 发送广播消息到所有连接
- 发送私聊消息到指定连接
- 发送JSON格式消息
-
服务管理
- 查看已注册的服务列表
- 监控服务状态
- 获取连接统计信息
-
实时日志
- 显示所有WebSocket事件
- 区分不同类型的日志消息
- 自动滚动和清理
reactphp-x/websocket-group
: WebSocket分组管理reactphp-x/register-center
: 服务注册中心monolog/monolog
: 日志记录
MIT License
欢迎提交Issue和Pull Request来改进这个项目。
- wpjscc - Initial work - wpjscc@gmail.com