服务端(异步风格) - TCP/UDP服务器 - 配置
> 本文摘自官方文档 https://wiki.swoole.com/#/server/setting , 手打
Server->set()
函数用于设置 Server
运行时的各项参数. > 从4.5.5版本开始, 底层会检查设置的配置项, 如果不是Swoole提供的配置项抛出Warning: "PHP Warning: unsupported option [xxx] in xxxx".
reactor_num
设置启动的 reactor
线程数 , 默认值: CPU核数( swoole_cpu_num
)。
用来设置主进程内事件处理线程的数量, 每个线程都会维持一个 EventLoop
. 线程之间是无锁的, 指令可以被所有cpu核心并行执行. 可设置线程数为 CPU核数( swoole_cpu_num
)的1-4倍。
worker_num
设置启动的Worker进程数 , 默认值: CPU核数( swoole_cpu_num
)。
- 如果业务代码是全异步IO的, 建议设置为CPU核数(
swoole_cpu_num
)的1-4倍 - 如果业务代码为同步IP, 需要根据请求响应时间和系统负载来调整, 例如: 100-500
- 默认设置为CPU核数(
swoole_cpu_num
), 但最大不得超过CPU核数的100倍
如果1个请求耗时100ms, 则一个进程处理速度是10Req/s, 要提供1000 QPS的处理能力, 至少要设置100个进程.
如何查看进程的内存占用? 这里我们只看 RSS(Resideng Set Size, 实际使用物理内存, 包含共享库占用的内存).
-
top
命令的第六列RES -
ps -aux
的第六列RSS -
cat /proc/进程id/status | grep RSS
, 比如: 我们通过ps -aux | grep redis
得到redis的进程id是2164, 然后我们查看redis的内存占用:cat /proc/2164/status | grep RSS
max_request
设置 worker
进程的最大任务数 。 默认值为0, 表示不会退出进程。
一个 worker
进程在处理完超过此数值的任务后将自动退出, 释放所有内存和资源。
该参数的主要作用是解决由于程序编码不规范导致的PHP内存泄漏问题. 如果PHP应用程序有缓慢的内存泄漏, 但是无法确定到具体的原因,无法解决, 则可以通过设置
max_request
临时 解决. 可以通过Swoole_Tracker
发现泄漏的代码.
注意:
- 达到
max_request
不一定马上关闭进程, 这与max_wait_time
(进程收到停止服务通知后最大等待时间, 默认3s)有关. - 在SWOOLE_BASE模式下, 达到max_request重启进程会导致客户端连接断开. > 当worker进程内发生致命错误或者人工执行exit时进程自动退出. master进程会重新启动一个新的worker进程来继续处理请求.
max_conn (max_connection)
服务器的最大允许的连接数 . 默认值 ulimit -n
, 取值范围: [ (worker_num + task_worker_num) * 2 + 32
, 100万].
- 内存占用: 一个TCP连接的
Connection
信息占用224 Bytes
。
task_worker_num
配置 Task
进程的数量 . 最大值: CPU核数( swoole_cpu_num
)的1000倍
因为task功能不会自动启动, 必须通过设置该参数才会启动, 且 必须 注册 onTask
, onFinish
这两个回调函数.
Task进程是同步阻塞的, 所以需要根据单个task的处理耗时和投递的速度来设置进程数.
task_worker_num = 任务投递的速度 / 每秒可处理的任务数
> Task进程内不能使用 Swoole\Server->task
方法
task_ipc_mode
设置 Task
进程与 Worker
进程之间通信的方式 . 默认值: 1, 表示使用 Unix_socket
通信.
模式:
- 1: 使用
Unix_socket
通信(默认) - 2: 使用
sysvmsg
消息队列通信 - 3: 使用
sysvmsg
消息队列通信, 并设置为争抢模式
提示:
- 模式1:
- 使用模式1时, 支持定向投递, 可在task和taskwait方法中使用
dst_worker_id
指定目标Task进程. -
dst_workder_id
使用默认值-1时, 底层会判断每个Task进程的状态, 向当前空闲的进程投递任务.
- 使用模式1时, 支持定向投递, 可在task和taskwait方法中使用
- 默认2,3
- 消息队列模式使用os提供的内存队列存储数据, 未指定
message_queue_key
消息队列key
, 将使用私有队列, 在Server
程序终止后会删除消息队列. - 指定消息队列
key
后Server
程序终止后, 消息队列中的数据不会删除, 因此进程重启后仍然能取到数据. - 可使用
ipcrm -q 消息队列ID
手动删除消息队列数据 -
模式2
和模式3
的不同之处是:模式2
支持定向投递,$server->task($data, $task_worker_id)
可以指定要投递的task进程id.模式3
是完全争抢模式, task进程会争抢队列, 无法使用定向投递,task/taskwait
将无法指定目标进程id, 即使指定了也无效.
- 消息队列模式使用os提供的内存队列存储数据, 未指定
task_max_request
设置task进程的最大任务数 , 默认值: 0.
一个task进程在处理完超过此数值的任务后将自动退出. 这个参数是为了防止PHP进程内存溢出. 如果不希望进程自动退出, 可以使用默认的设置(0).
task_tmpdir
设置task的数据临时目录 . 默认值: Linux /tmp
目录(内存文件系统).
在 Server
中, 如果投递的数据超过 8180
Bytes, 将启用临时文件来保存数据, 这些数据就保存在 task_tmpdir
设置的目录.
如果设置的 task_tmpdir
目录不存在, 底层会自动创建, 如果创建失败, Server->start
失败!
task_enable_coroutine
开启 Task
协程支持. 默认值: false
, 自v4.2.12起支持.
- 必须在
Server->set()
中设置enable_coroutine=true
:
$server->set([
'enable_coroutine' => true,
....
]);
- 开启后自动在
onTask
回调中创建协程和协程容器, PHP代码可以直接使用协程API; - 未开启时仅支持同步阻塞.
不完整示例:
- 普通模式
$server->on('task', function(Swoole\Server $server, $task_id, $reactor_id, $data){
// do something
});
- 使用面向对象风格的 Task 回调格式(设置task_use_object=true)
$server->on('task', function(Swoole\Server $server, Swoole\Server\Task $task){
echo 'Worker进程id: ' . $task->worker_id . PHP_EOL;
echo '任务编号: ' . $task->id . PHP_EOL;
echo '任务类型: ' . $task->flags . PHP_EOL;//taskwait,task, taskCo, taskWaitMulti 可能使用不同的flags
echo '任务的数据: ' . $task->data . PHP_EOL;
echo '投递时间: ' . $task->dispatch_time . PHP_EOL;
//协程API
co::sleep(0.2);
//完成任务, 结束并返回
$task->finish([123, 'hello']);
});
task_use_object / task_object
是否 使用面向对象风格的Task回调格式 , 别名 task_object
是v4.6.0新增的. 默认: false
.
设置为 true
时, onTask
回调将变成对象模式.
示例:
$server = new Swoole\Server('127.0.0.1', 9501);
$server->set([
'worker_num' => 1,
'task_worker_num' => 3, //启动task
'task_use_object' => true,//别名 task_object, 从v4.6起支持
]);
$server->on('receive', function(Swoole\Server $server, $fd, $task_id, $data){
echo 'task_id=' . $task_id . ' 收到来自fd=' . $fd . '的数据: ' . trim($data) . PHP_EOL;
//投递任务
$server->task(['fd'=>$fd]);
});
//task收到任务: onTask事件回调, 返回对象模式
$server->on('task', function(Swoole\Server $server, Swoole\Server\Task $task){
//给客户端发送信息
$server->send($task->data['fd'], json_encode($server->stats()));
//task任务完成, 通知Worker进程, 执行onFinish事件回调
$server->finish($task->data);
});
//task任务完成
$server->on('finish', function(Swoole\Server $server, $task_id, $data){
echo 'task_id=' . $task_id . ' finished';
});
$server->start();
dispatch_mode
数据包分发策略 . 默认值: 2 (固定模式, 根据$fd分配Worker)
模式:
-
1: 轮循模式: 收到会轮循, 依次分配给每一个
Worker
进程 -
2: 固定模式: 根据
$fd
分配Worker
, 保证同一个连接发来的数据只会被同一个Worker
处理. -
3: 抢占模式: 主进程会根据
Worker
的忙闲状态选择投递, 只会投递给处于闲置状态的Worker
. -
4: IP分配: 根据客户端IP进行取模(取余), 分配给一个固定的Worker进程. 可以保证同一个来源IP的数据总会被分配到同一个
Worker
进程. 算法为:ip2long(ip) % worker_num
. -
5: UID分配: 需要用户代码中调用
Server->bind()
将一个连接绑定一个uid
, 然后底层根据uid的值分配到不同的Worker
进程, 算法为:UID % worker_num
, 如果UID是字符串, 则crc32(string UID)
-
7: stream模式: 空闲的
Worker
进程会accept
(接受)请求, 并accept(接受)Reactor
的新请求. 提示 : -
使用建议:
- 无状态
Server
可以使用1
或3
, 同步阻塞Server
使用3
, 异步Server
使用1
. - 有状态
Server
使用2
,4
,5
- 无状态
-
UDP 协议
-
dispatch_mode=2, 4, 5
时为 固定 分配,底层使用客户端ip 取模 散列到不同的Worker进程, 算法为:ip2long(ip) % worker_num
. -
dispatch_mode=1, 3
时 随机 分配到不同
-