- 承载客户端的websocket连接,并支持存储连接的:业务系统内唯一id、订阅的主题、连接登录后的session信息
- 承载业务进程的tcp连接
- 接收客户端连接发来的数据,并将数据转发给业务进程
- 接收业务进程发来的数据,并按数据中包含的cmd指令:或转发给客户端连接,或自己处理并返回给业务进程
netsvr能起到桥梁作用,让你的业务进程和客户端之间进行通信,从而实现:
- 你的业务进程:以单播、批量单播、组播、发布、批量发布、广播等形式,将数据主动下发给客户端连接
- 你的客户端:订阅主题的数据、取消订阅主题、发送数据给服务端、接收服务端下发的数据
更多功能请阅读:业务进程与netsvr进程的交互协议
- hyperf框架 下的sdk:composer require buexplain/netsvr-business-coroutine
php-fpm、ThinkPHP、Laravel、Laravel Octane、Webman等非协程模式运行的sdk:composer require buexplain/netsvr-business-serial- go 语言的sdk:github.com/buexplain/netsvr-business-go/v2
下面是php语言的laravel框架下的websocket打开和关闭的回调接口示例:
<?php
namespace App\Http\Controllers;
use Exception;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Log;
use NetsvrProtocol\ConnClose;
use NetsvrProtocol\ConnOpen;
use NetsvrProtocol\ConnOpenResp;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
/**
* websocket打开和关闭的回调接口示例
* 注册路由的时候一定要注册成post请求,并且加入到VerifyCsrfToken中间件的except属性中
* composer require google/protobuf
* composer require buexplain/netsvr-protocol-php
*/
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
/**
* websocket连接打开的回调接口
* @param Request $request
* @return SymfonyResponse
* @throws Exception
*/
public function onopen(Request $request): SymfonyResponse
{
$protobuf = $request->getContent();
$cp = new ConnOpen();
$cp->mergeFromString($protobuf);
Log::debug('onopen -->' . $cp->serializeToJsonString());
$cpResp = new ConnOpenResp();
$cpResp->setAllow(true);
$cpResp->setData('欢迎登录,已为您订阅法律相关栏目: ' . $cp->serializeToJsonString());
$cpResp->setNewSession(json_encode([
'id' => 1,
'nickname' => '法外狂徒',
]));
$cpResp->setNewCustomerId("1");
$cpResp->setNewTopics([
'法治在线栏目',
'法考专题栏目'
]);
return response($cpResp->serializeToString(), 200, [
'Content-Type' => 'application/x-protobuf',
]);
}
/**
* websocket连接关闭的回调接口
* @param Request $request
* @return SymfonyResponse
* @throws Exception
*/
public function onclose(Request $request): SymfonyResponse
{
$protobuf = $request->getContent();
$cc = new ConnClose();
$cc->mergeFromString($protobuf);
Log::debug('onclose -->' . $cc->serializeToJsonString());
return response('', 204);
}
}- netsvr.go 是netsvr程序的启动入口。
- business.go 是为了测试netsvr程序的业务进程启动入口。
- stress.go 是为了压测netsvr程序的压测进程启动入口,它可以大规模的向网关发起websocket连接。
- build.sh 是编译脚本,把项目clone下来后,直接跑它(依赖go环境),会自动编译出Windows、Linux、Mac三端的netsvr程序、业务进程程序、压测进程程序。
启动顺序是:netsvr --> 业务进程 --> 压测进程
注意:生产环境只需要netsvr程序
场景:
- 40个群,每个群2500连接,目标连接数是100000
- 每个连接都是登录状态,绑定:1个userId、2KiB的session
- 每个群每秒发送3条100B(纯文本内容、不含协议/元数据)的群消息
以上场景要求对应stress.toml中的配置如下:
# 群聊,登录、订阅、发消息
[GroupChat]
Enable = true
# 模拟登录后存储的session长度
SessionLen = 2048
# 群数量
GroupNum = 10
# 发送的消息大小
MessageLen = 100
# 发送消息限流控制,示例:每5秒最多1个请求=1/5,每秒最多3个请求=3
Limit = 3
# 每个群的人数
Step = [
{ ConnNum = 2500, ConnectNum = 100, Suspend = 10 },
]机器配置:
| 配置项 | 规格值 |
|---|---|
| 实例类型 | 腾讯云CVM服务器:X86计算、标准型、SA9 |
| 规格代码 | SA9.LARGE8 |
| vCPU | 4 核 |
| 内存 | 8 GiB |
| 处理器主频/睿频 | - / 3.4 GHz |
| 处理器型号 | AMD EPYC Turin-D (-/3.4GHz) |
| 内网带宽 | 2 Gbps |
| 内网每秒包转发数量 | 30 万 PPS |
部署情况:
1台部署netsvr程序,2台机器部署business程序,4台机器部署stress程序。
程序运行后,实际压测结果如下:
| 项 | 值 |
|---|---|
| 实际连接数 | 100000 |
| 绑定userId数 | 100000 |
| 群数 | 40 |
| netsvr程序接收消息并发数平均值(1分钟) | 121 msg/s |
| netsvr程序下发消息并发数平均值(1分钟) | 303544 msg/s |
系统监控信息 (top):
- 系统概况
| 指标 | 数值 |
|---|---|
| 时间 | 18:03:44 |
| 运行时间 | up 3:00 |
| 用户数 | 0 user |
| 负载平均值 (1分钟) | 5.03 |
| 负载平均值 (5分钟) | 5.29 |
| 负载平均值 (15分钟) | 5.03 |
- 任务统计
| 总计 | 运行中 | 睡眠 | 停止 | 僵尸进程 |
|---|---|---|---|---|
| 1 | 1 | 0 | 0 | 0 |
- CPU 使用率
| 用户态(us) | 系统态(sy) | 优化(ni) | 空闲(id) | 等待(wa) | 硬件中断(hi) | 软件中断(si) | 窃取(st) |
|---|---|---|---|---|---|---|---|
| 19.6% | 53.0% | 0.0% | 1.5% | 0.0% | 0.0% | 25.9% | 0.0% |
- 内存使用 (MiB)
| 类型 | 总计 | 空闲 | 已用 | 缓冲/缓存 | 可用 |
|---|---|---|---|---|---|
| 内存 | 7555.1 | 5486.6 | 1852.0 | 523.1 | 5703.1 |
| Swap | 0.0 | 0.0 | 0.0 | - | - |
netsvr程序进程详情
| PID | USER | PR | NI | VIRT | RES | SHR | S | %CPU | %MEM | TIME+ | COMMAND |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 36245 | root | 20 | 0 | 3155892 | 921876 | 5808 | R | 379.4 | 11.9 | 115:56.25 | netsvr-linux-am |
关键指标分析:
- CPU 负载与系统开销极高:
- 1分钟负载高达 5.03,且系统空闲率 (
id) 仅剩 1.5%,CPU 几乎满负荷运转。 - 核心问题: 系统态
sy飙升至 53.0%,软中断si高达 25.9%。两者相加近 80%。 - 相比之下,用户态
us仅占 19.6%。这表明 CPU 绝大部分时间都在内核空间处理网络数据包的收发(软中断)和系统调用。 - 这是一个典型的严重网络瓶颈表现,可能由于网络流量过大导致内核处理不过来,或者存在网络中断风暴。
- 1分钟负载高达 5.03,且系统空闲率 (
- 进程占用:
netsvr-linux-am进程占用了 379.4% 的 CPU(接近 4 个核心的满载)。
- 内存状态:
- 内存资源非常充裕,总内存 7555MB,已用 1852MB,可用高达 5703MB。
- 性能瓶颈完全在 CPU 和网络处理上,而非内存不足。
压测场景分析:
- websocket服务器几乎不接收消息
- websocket服务器只下发消息
- websocket服务器下发消息速度是:每个连接每秒下发3条消息
- 4核8GiB配置的机器,承受100000个连接,每个连接每秒下发3条消息,在此场景下是处于极限负载状态
- websocket服务器对内存的需求不高,对cpu算力和网卡性能要求高
负载估算:
| 机器配置 | 极限负载下连接数 | 峰值负载下连接数 | 安全生产下连接数 |
|---|---|---|---|
| 4核8GiB | 10万 | 8万 | 7万 |
| 8核16GiB | 20万 | 16万 | 14万 |
| 16核16GiB | 40万 | 32万 | 28万 |
| 32核64GiB | 80万 | 64万 | 56万 |
