当前位置: 首页 > 文档资料 > Swoole 中文文档 >

配置

优质
小牛编辑
145浏览
2023-12-01

Server::set() 函数用于设置Server运行时的各项参数。本节所有的子页面均为配置数组的元素。

reactor_num

?> 设置启动的 Reactor 线程数。【默认值:CPU核数】

?> 通过此参数来调节主进程内事件处理线程的数量,以充分利用多核。默认会启用CPU核数相同的数量。
Reactor线程是可以利用多核,如:机器有128核,那么底层会启动128线程。
每个线程能都会维持一个EventLoop。线程之间是无锁的,指令可以被128CPU并行执行。
考虑到操作系统调度存在一定程度的性能损失,可以设置为CPU核数*2,以便最大化利用CPU的每一个核。

  • 提示

    • reactor_num建议设置为CPU核数的1-4
    • reactor_num最大不得超过 swoole_cpu_num() * 4
  • 注意

!> -reactor_num必须小于或等于worker_num
-如果设置的reactor_num大于worker_num,会自动调整使reactor_num等于worker_num
-在超过8核的机器上reactor_num默认设置为8

worker_num

?> 设置启动的Worker进程数。【默认值:CPU核数】

?> 如1个请求耗时100ms,要提供1000QPS的处理能力,那必须配置100个进程或更多。
但开的进程越多,占用的内存就会大大增加,而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。

  • 提示
    • 如果业务代码是全异步IO的,这里设置为CPU核数的1-4倍最合理
    • 如果业务代码为同步IO,需要根据请求响应时间和系统负载来调整,例如:100-500
    • 默认设置为swoole_cpu_num(),最大不得超过swoole_cpu_num() * 1000
    • 假设每个进程占用40M内存,100个进程就需要占用4G内存,如何正确查看进程的内存占用请参考Swoole官方视频教程

max_request

?> 设置worker进程的最大任务数。【默认值:0 即不会退出进程】

?> 一个worker进程在处理完超过此数值的任务后将自动退出,进程退出后会释放所有内存和资源

!> 这个参数的主要作用是解决由于程序编码不规范导致的PHP进程内存泄露问题。PHP应用程序有缓慢的内存泄漏,但无法定位到具体原因、无法解决,可以通过设置max_request临时解决,需要找到内存泄漏的代码并修复,而不是通过此方案,可以使用Swoole Tracker发现泄漏的代码。

  • 提示

    • 达到max_request不一定马上关闭进程,参考max_wait_time
    • SWOOLE_BASE下,达到max_request重启进程会导致客户端连接断开。

!> 当worker进程内发生致命错误或者人工执行exit时,进程会自动退出。master进程会重新启动一个新的worker进程来继续处理请求

max_conn (max_connection)

?> 服务器程序,最大允许的连接数。【默认值:ulimit -n

?> 如max_connection => 10000, 此参数用来设置Server最大允许维持多少个TCP连接。超过此数量后,新进入的连接将被拒绝。

  • 提示

    • 默认设置

      • 应用层未设置max_connection,底层将使用ulimit -n的值作为缺省设置
      • 4.2.9或更高版本,当底层检测到ulimit -n超过100000时将默认设置为100000,原因是某些系统设置了ulimit -n100万,需要分配大量内存,导致启动失败
    • 最大上限

      • 请勿设置max_connection超过1M
    • 最小设置

      • 此选项设置过小底层会抛出错误,并设置为ulimit -n的值。
      • 最小值为(worker_num + task_worker_num) * 2 + 32
        serv->max_connection is too small.
    • 内存占用

      • max_connection参数不要调整的过大,根据机器内存的实际情况来设置。Swoole会根据此数值一次性分配一块大内存来保存Connection信息,一个TCP连接的Connection信息,需要占用224字节。
  • 注意

!> max_connection最大不得超过操作系统ulimit -n的值,否则会报一条警告信息,并重置为ulimit -n的值

  WARN    swServer_start_check: serv->max_conn is exceed the maximum value[100000].

task_worker_num

?> 配置 Task进程的数量。【默认值:未配置则不启动 task

?> 配置此参数后将会启用task功能。所以Server务必要注册onTaskonFinish 2 个事件回调函数。如果没有注册,服务器程序将无法启动。

  • 提示

    • Task进程是同步阻塞的

    • 最大值不得超过swoole_cpu_num() * 1000

    • 计算方法

    • 单个task的处理耗时,如100ms,那一个进程1秒就可以处理1/0.1=10个task

    • task投递的速度,如每秒产生2000task

    • 2000/10=200,需要设置task_worker_num => 200,启用200个Task进程

  • 注意

    !> - Task进程内不能使用Swoole\Server->task方法

task_ipc_mode

?> 设置 Task进程Worker进程之间通信的方式。【默认值:1

?> 请先阅读Swoole下的IPC通讯

模式作用
1使用Unix Socket通信【默认模式】
2使用sysvmsg消息队列通信
3使用sysvmsg消息队列通信,并设置为争抢模式
  • 提示

    • 模式1

      • 使用模式1时,支持定向投递,可在tasktaskwait方法中使用dst_worker_id,指定目标 Task进程
      • dst_worker_id设置为-1时,底层会判断每个 Task进程的状态,向当前状态为空闲的进程投递任务。
    • 模式23

      • 消息队列模式使用操作系统提供的内存队列存储数据,未指定 mssage_queue_key 消息队列Key,将使用私有队列,在Server程序终止后会删除消息队列。
      • 指定消息队列KeyServer程序终止后,消息队列中的数据不会删除,因此进程重启后仍然能取到数据
      • 可使用ipcrm -q消息队列ID手工删除消息队列数据
      • 模式2模式3的不同之处是,模式2支持定向投递,$serv->task($data, $task_worker_id) 可以指定投递到哪个 task进程模式3是完全争抢模式, task进程会争抢队列,将无法使用定向投递,task/taskwait将无法指定目标进程ID,即使指定了$task_worker_id,在模式3下也是无效的。
  • 注意

    !> -模式3会影响sendMessage方法,使sendMessage发送的消息会随机被某一个 task进程获取。
    -使用消息队列通信,如果 Task进程 处理能力低于投递速度,可能会引起Worker进程阻塞。
    -使用消息队列通信后task进程无法支持协程(开启task_enable_coroutine)。

task_max_request

设置task进程的最大任务数。一个task进程在处理完超过此数值的任务后将自动退出。这个参数是为了防止PHP进程内存溢出。如果不希望进程自动退出可以设置为0。

?> 设置 task进程的最大任务数。【默认值:0

task_tmpdir

?> 设置task的数据临时目录。【默认值:Linux /tmp 目录】

?> 在Server中,如果投递的数据超过8180字节,将启用临时文件来保存数据。这里的task_tmpdir就是用来设置临时文件保存的位置。

  • 提示

    • 底层默认会使用/tmp目录存储task数据,如果你的Linux内核版本过低,/tmp目录不是内存文件系统,可以设置为 /dev/shm/
    • task_tmpdir目录不存在,底层会尝试自动创建
  • 注意

    !> -创建失败时,Server->start会失败

task_enable_coroutine

?> 开启 Task 协程支持。【默认值:false】,v4.2.12起支持

?> 开启后自动在onTask回调中创建协程和协程容器PHP代码可以直接使用协程API

  • 示例

    $server->on('Task', function ($serv, Swoole\Server\Task $task) {
        //来自哪个 Worker 进程
        $task->worker_id;
        //任务的编号
        $task->id;
        //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags
        $task->flags;
        //任务的数据
        $task->data;
        //协程 API
        co::sleep(0.2);
        //完成任务,结束并返回数据
        $task->finish([123, 'hello']);
    });
  • 注意

    !> -task_enable_coroutine必须在enable_coroutinetrue时才可以使用
    -开启task_enable_coroutineTask工作进程支持协程
    -未开启task_enable_coroutine,仅支持同步阻塞

task_use_object

?> 使用面向对象风格的Task回调格式。【默认值:false

?> 设置为true时,onTask回调将变成对象模式。

  • 示例

    <?php
    
    $server = new Swoole\Server('127.0.0.1', 9501);
    $server->set([
        'worker_num'      => 1,
        'task_worker_num' => 3,
        'task_use_object' => true,
    ]);
    $server->on('receive', function (Swoole\Server $server, $fd, $tid, $data) {
        $server->task(['fd' => $fd,]);
    });
    $server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) {
        //此处$task是Swoole\Server\Task对象
        $server->send($task->data['fd'], json_encode($server->stats()));
    
    });
    $server->start();

dispatch_mode

?> 数据包分发策略。【默认值:2

模式值模式作用
1轮循模式收到会轮循分配给每一个Worker进程
2固定模式根据连接的文件描述符分配Worker。这样可以保证同一个连接发来的数据只会被同一个Worker处理
3抢占模式主进程会根据Worker的忙闲状态选择投递,只会投递给处于闲置状态的Worker
4IP分配根据客户端IP进行取模hash,分配给一个固定的Worker进程。
可以保证同一个来源IP的连接数据总会被分配到同一个Worker进程。算法为 ip2long(ClientIP) % worker_num
5UID分配需要用户代码中调用 Server->bind() 将一个连接绑定1uid。然后底层根据UID的值分配到不同的Worker进程。
算法为 UID % worker_num,如果需要使用字符串作为UID,可以使用crc32(UID_STRING)
7stream模式空闲的Workeraccept连接,并接受Reactor的新请求
  • 提示

    • 使用建议

      • 无状态Server可以使用13,同步阻塞Server使用3,异步非阻塞Server使用1
      • 有状态使用245
    • UDP协议

      • dispatch_mode=2/4/5时为固定分配,底层使用客户端IP取模散列到不同的Worker进程,算法为 ip2long(ClientIP) % worker_num
      • dispatch_mode=1/3时随机分配到不同的Worker进程
    • BASE模式

      • dispatch_mode配置在 SWOOLE_BASE 模式是无效的,因为BASE不存在投递任务,当收到客户端发来的数据后会立即在当前线程/进程回调onReceive,不需要投递Worker进程。
  • 注意

    !> -dispatch_mode=1/3时,底层会屏蔽onConnect/onClose事件,原因是这2种模式下无法保证onConnect/onClose/onReceive的顺序;
    -非请求响应式的服务器程序,请不要使用模式13。例如:http服务就是响应式的,可以使用13,有TCP长连接状态的就不能使用13

dispatch_func

?> 设置dispatch函数,Swoole底层内置了6dispatch_mode,如果仍然无法满足需求。可以使用编写C++函数或PHP函数,实现dispatch逻辑。

  • 使用方法

    $server->set(array(
      'dispatch_func' => 'my_dispatch_function',
    ));
  • 提示

    • 设置dispatch_func后底层会自动忽略dispatch_mode配置
    • dispatch_func对应的函数不存在,底层将抛出致命错误
    • 如果需要dispatch一个超过8K的包,dispatch_func只能获取到 0-8180 字节的内容
  • 编写PHP函数

    ?> 由于ZendVM无法支持多线程环境,即使设置了多个Reactor线程,同一时间只能执行一个dispatch_func。因此底层在执行此PHP函数时会进行加锁操作,可能会存在锁的争抢问题。请勿在dispatch_func中执行任何阻塞操作,否则会导致Reactor线程组停止工作。

    $server->set(array(
        'dispatch_func' => function ($server, $fd, $type, $data) {
            var_dump($fd, $type, $data);
            return intval($data[0]);
        },
    ));
    • $fd为客户端连接的唯一标识符,可使用Server::getClientInfo获取连接信息
    • $type数据的类型,0表示来自客户端的数据发送,4表示客户端连接建立,3表示客户端连接关闭
    • $data数据内容,需要注意:如果启用了HttpEOFLength等协议处理参数后,底层会进行包的拼接。但在dispatch_func函数中只能传入数据包的前8K内容,不能得到完整的包内容。
    • 必须返回一个0 - (server->worker_num - 1)的数字,表示数据包投递的目标工作进程ID
    • 小于0或大于等于server->worker_num为异常目标IDdispatch的数据将会被丢弃
  • 编写C++函数

    在其他PHP扩展中,使用swoole_add_function注册长度函数到Swoole引擎中。

    ?> C++函数调用时底层不会加锁,需要调用方自行保证线程安全性

    int dispatch_function(swServer *serv, swConnection *conn, swEventData *data);
    
    int dispatch_function(swServer *serv, swConnection *conn, swEventData *data)
    {
        printf("cpp, type=%d, size=%d\n", data->info.type, data->info.len);
        return data->info.len % serv->worker_num;
    }
    
    int register_dispatch_function(swModule *module)
    {
        swoole_add_function("my_dispatch_function", (void *) dispatch_function);
    }
    • dispatch函数必须返回投递的目标worker进程id
    • 返回的worker_id不得超过server->worker_num,否则底层会抛出段错误
    • 返回负数(return -1)表示丢弃此数据包
    • data可以读取到事件的类型和长度
    • conn是连接的信息,如果是UDP数据包,connNULL
  • 注意

    !> -dispatch_func仅在SWOOLE_PROCESS模式下有效,UDP/TCP/UnixSocket类型的服务器均有效
    -返回的worker_id不得超过server->worker_num,否则底层会抛出段错误

message_queue_key

?> 设置消息队列的KEY【默认值:ftok($php_script_file, 1)

?> 仅在task_ipc_mode = 2/3时使用。设置的Key仅作为Task任务队列的KEY,参考Swoole下的IPC通讯

?> task队列在server结束后不会销毁,重新启动程序后, task进程仍然会接着处理队列中的任务。如果不希望程序重新启动后执行旧的Task任务。可以手工删除此消息队列。

ipcs -q 
ipcrm -Q [msgkey]

daemonize

?> 守护进程化【默认值:0

?> 设置daemonize => 1时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。
如果不启用守护进程,当ssh终端退出后,程序将被终止运行。

  • 提示

    • 启用守护进程后,标准输入和输出会被重定向到 log_file

    • 如果未设置log_file,将重定向到 /dev/null,所有打印屏幕的信息都会被丢弃

    • 启用守护进程后,CWD(当前目录)环境变量的值会发生变更,相对路径的文件读写会出错。PHP程序中必须使用绝对路径

    • systemd

      • 使用systemd或者supervisord管理Swoole服务时,请勿设置daemonize = 1。主要原因是systemd的机制与init不同。init进程的PID1,程序使用daemonize后,会脱离终端,最终被init进程托管,与init关系变为父子进程关系。
      • systemd是启动了一个单独的后台进程,自行fork管理其他服务进程,因此不需要daemonize,反而使用了daemonize = 1会使得Swoole程序与该管理进程失去父子进程关系。

backlog

?> 设置Listen队列长度【默认值:】

?> 如backlog => 128,此参数将决定最多同时有多少个等待accept的连接。

  • 关于tcpbacklog

    ?> 我们知道tcp有三次握手的过程,客户端 syn=>服务端 syn+ack=>客户端 ack,当服务器收到客户端的ack后会将连接放到一个叫做accept queue的队列里面(注1),
    队列的大小由backlog参数和配置somaxconn 的最小值决定,我们可以通过ss -lt命令查看最终的accept queue队列大小,Swoole的主进程调用accept(注2)
    accept queue里面取走。 当accept queue满了之后连接有可能成功(注4),
    也有可能失败,失败后客户端的表现就是连接被重置(注3)
    或者连接超时,而服务端会记录失败的记录,可以通过 netstat -s|grep 'times the listen queue of a socket overflowed 来查看日志。如果出现了上述现象,你就应该调大该值了。 幸运的是Swoole的SWOOLE_PROCESS模式与PHP-FPM/Apache等软件不同,并不依赖backlog来解决连接排队的问题。所以基本不会遇到上述现象。

    • 注1:linux2.2之后握手过程分为syn queueaccept queue两个队列, syn queue长度由tcp_max_syn_backlog决定。
    • 注2:高版本内核调用的是accept4,为了节省一次set no block系统调用。
    • 注3:客户端收到syn+ack包就认为连接成功了,实际上服务端还处于半连接状态,有可能发送rst包给客户端,客户端的表现就是Connection reset by peer
    • 注4:成功是通过tcp的重传机制,相关的配置有tcp_synack_retriestcp_abort_on_overflow。想深入学习底层TCP机制可以看Swoole官方视频教程

log_file

?> 指定Swoole错误日志文件【默认值:】

?> 在Swoole运行期发生的异常信息会记录到这个文件中,默认会打印到屏幕。
开启守护进程模式后(daemonize => true),标准输出将会被重定向到log_file。在PHP代码中echo/var_dump/print等打印到屏幕的内容会写入到log_file文件。

  • 提示

    • log_file中的日志仅仅是做运行时错误记录,没有长久存储的必要。

    • 日志标号

      ?> 在日志信息中,进程ID前会加一些标号,表示日志产生的线程/进程类型。

      • # Master进程
      • $ Manager进程
      • * Worker进程
      • ^ Task进程
    • 重新打开日志文件

      ?> 在服务器程序运行期间日志文件被mv移动或unlink删除后,日志信息将无法正常写入,这时可以向Server发送SIGRTMIN信号实现重新打开日志文件。

  • 注意

    !> -log_file不会自动切分文件,所以需要定期清理此文件。观察log_file的输出,可以得到服务器的各类异常信息和警告。

log_level

?> 设置Server错误日志打印的等级,范围是0-6。低于log_level设置的日志信息不会抛出。【默认值:SWOOLE_LOG_DEBUG

对应级别常量参考日志等级

  • 注意

    !> SWOOLE_LOG_DEBUGSWOOLE_LOG_TRACE仅在编译为--enable-debug-log--enable-trace-log版本时可用,默认为SWOOLE_LOG_DEBUG也就是所有级别都打印;
    在开启daemonize守护进程时,底层将把程序中的所有打印屏幕的输出内容写入到log_file,这部分内容不受log_level控制。

open_tcp_keepalive

?> 在TCP中有一个Keep-Alive的机制可以检测死连接,应用层如果对于死链接周期不敏感或者没有实现心跳机制,可以使用操作系统提供的keepalive机制来踢掉死链接。 在 Server::set() 配置中增加open_tcp_keepalive=>1表示启用tcp keepalive。 另外,有3个选项可以对keepalive的细节进行调整,参考Swoole官方视频教程

  • 选项

    • __tcp_keepidle__

      单位秒,连接在n秒内没有数据请求,将开始对此连接进行探测。

    • __tcp_keepcount__

      探测的次数,超过次数后将close此连接。

    • __tcp_keepinterval__

      探测的间隔时间,单位秒。

  • 示例

$serv = new Swoole\Server("192.168.2.194", 6666, SWOOLE_PROCESS);
$serv->set(array(
    'worker_num' => 1,
    'open_tcp_keepalive' => 1,
    'tcp_keepidle' => 4, //4s没有数据传输就进行检测
    'tcp_keepinterval' => 1, //1s探测一次
    'tcp_keepcount' => 5, //探测的次数,超过5次后还没回包close此连接
));

$serv->on('connect', function ($serv, $fd) {
    var_dump("Client:Connect $fd");
});

$serv->on('receive', function ($serv, $fd, $reactor_id, $data) {
    var_dump($data);
});

$serv->on('close', function ($serv, $fd) {
  var_dump("close fd $fd");
});

$serv->start();

heartbeat_check_interval

?> 启用心跳检测【默认值:false

?> 此选项表示每隔多久轮循一次,单位为秒。如 heartbeat_check_interval => 60,表示每60秒,遍历所有连接,如果该连接在120秒内(heartbeat_idle_time未设置时默认为interval的两倍),没有向服务器发送任何数据,此连接将被强制关闭。若未配置,则不会启用心跳, 该配置默认关闭,参考Swoole官方视频教程

  • 提示

    • Server并不会主动向客户端发送心跳包,而是被动等待客户端发送心跳。服务器端的heartbeat_check仅仅是检测连接上一次发送数据的时间,如果超过限制,将切断连接。
    • 被心跳检测切断的连接依然会触发onClose事件回调
  • 注意

    !> heartbeat_check仅支持TCP连接

heartbeat_idle_time

?> 连接最大允许空闲的时间【默认值:】

?> 需要与heartbeat_check_interval配合使用

array(
    'heartbeat_idle_time'      => 600, // 表示一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭
    'heartbeat_check_interval' => 60,  // 表示每60秒遍历一次
);
  • 提示
    • 启用heartbeat_idle_time后,服务器并不会主动向客户端发送数据包
    • 如果只设置了heartbeat_idle_time未设置heartbeat_check_interval底层将不会创建心跳检测线程,PHP代码中可以调用heartbeat方法手工处理超时的连接

open_eof_check

?> 打开EOF检测【默认值:false】,参考TCP粘包问题

?> 此选项将检测客户端连接发来的数据,当数据包结尾是指定的字符串时才会投递给Worker进程。否则会一直拼接数据包,直到超过缓存区或者超时才会中止。当出错时底层会认为是恶意连接,丢弃数据并强制关闭连接。
常见的Memcache/SMTP/POP等协议都是以\r\n结束的,就可以使用此配置。开启后可以保证Worker进程一次性总是收到一个或者多个完整的数据包。

array(
    'open_eof_check' => true,   //打开EOF检测
    'package_eof'    => "\r\n", //设置EOF
)
  • 注意

    !> 此配置仅对STREAM(流式的)类型的Socket有效,如 TCP 、Unix Socket Stream
    EOF检测不会从数据中间查找eof字符串,所以Worker进程可能会同时收到多个数据包,需要在应用层代码中自行explode("\r\n", $data) 来拆分数据包

open_eof_split

?> 启用EOF自动分包

?> 当设置open_eof_check后,可能会产生多条数据合并在一个包内 , open_eof_split参数可以解决这个问题,参考TCP粘包问题

?> 设置此参数需要遍历整个数据包的内容,查找EOF,因此会消耗大量CPU资源。假设每个数据包为2M,每秒10000个请求,这可能会产生20GCPU字符匹配指令。

array(
    'open_eof_split' => true,   //打开EOF_SPLIT检测
    'package_eof'    => "\r\n", //设置EOF
)
  • 提示

    • 启用open_eof_split参数后,底层会从数据包中间查找EOF,并拆分数据包。onReceive每次仅收到一个以EOF字串结尾的数据包。

    • 启用open_eof_split参数后,无论参数open_eof_check是否设置,open_eof_split都将生效。

      • open_eof_check 的差异
        • open_eof_check 只检查接收数据的末尾是否为 EOF,因此它的性能最好,几乎没有消耗
        • open_eof_check 无法解决多个数据包合并的问题,比如同时发送两条带有 EOF 的数据,底层可能会一次全部返回
        • open_eof_split 会从左到右对数据进行逐字节对比,查找数据中的 EOF 进行分包,性能较差。但是每次只会返回一个数据包

package_eof

?> 设置EOF字符串。【默认值:】,参考TCP粘包问题

?> 需要与 open_eof_check 或者 open_eof_split 配合使用。

  • 注意

    !> package_eof最大只允许传入8个字节的字符串

open_length_check

?> 打开包长检测特性【默认值:false】,参考TCP粘包问题

?> 包长检测提供了固定包头+包体这种格式协议的解析。启用后,可以保证Worker进程onReceive每次都会收到一个完整的数据包。
长度检测协议,只需要计算一次长度,数据处理仅进行指针偏移,性能非常高,推荐使用

  • 提示

    • 长度协议提供了3个选项来控制协议细节。

      ?> 此配置仅对STREAM类型的Socket有效,如TCP、Unix Socket Stream

      • package_length_type

        ?> 包头中某个字段作为包长度的值,底层支持了10种长度类型。请参考 package_length_type

      • package_body_offset

        ?> 从第几个字节开始计算长度,一般有2种情况:

        • length的值包含了整个包(包头+包体),package_body_offset0
        • 包头长度为N字节,length的值不包含包头,仅包含包体,package_body_offset设置为N
      • package_length_offset

        ?> length长度值在包头的第几个字节。

        • 示例:
        struct
        {
            uint32_t type;
            uint32_t uid;
            uint32_t length;
            uint32_t serid;
            char body[0];
        }

    ?> 以上通信协议的设计中,包头长度为4个整型,16字节,length长度值在第3个整型处。因此package_length_offset设置为80-3字节为type4-7字节为uid8-11字节为length12-15字节为serid

    $server->set(array(
      'open_length_check'     => true,
      'package_max_length'    => 81920,
      'package_length_type'   => 'N',
      'package_length_offset' => 8,
      'package_body_offset'   => 16,
    ));

package_length_type

?> 长度值的类型,接受一个字符参数,与PHPpack 函数一致。

目前Swoole支持10种类型:

字符参数作用
c有符号、1字节
C无符号、1字节
s有符号、主机字节序、2字节
S无符号、主机字节序、2字节
n无符号、网络字节序、2字节
N无符号、网络字节序、4字节
l有符号、主机字节序、4字节(小写L)
L无符号、主机字节序、4字节(大写L)
v无符号、小端字节序、2字节
V无符号、小端字节序、4字节

package_length_func

?> 设置长度解析函数

?> 支持C++PHP2种类型的函数。长度函数必须返回一个整数。

返回数作用
返回0长度数据不足,需要接收更多数据
返回-1数据错误,底层会自动关闭连接
返回包长度值(包括包头和包体的总长度)底层会自动将包拼好后返回给回调函数
  • 提示

    • 使用方法

      ?> 实现原理是先读取一小部分数据,在这段数据内包含了一个长度值。然后将这个长度返回给底层。然后由底层完成剩余数据的接收并组合成一个包进行dispatch

    • PHP长度解析函数

      ?> 由于ZendVM不支持运行在多线程环境,因此底层会自动使用Mutex互斥锁对PHP长度函数进行加锁,避免并发执行PHP函数。在1.9.3或更高版本可用。

      !> 请勿在长度解析函数中执行阻塞IO操作,可能导致所有Reactor线程发生阻塞

      $server = new Swoole\Server("127.0.0.1", 9501);
      
      $server->set(array(
          'open_length_check'   => true,
          'dispatch_mode'       => 1,
          'package_length_func' => function ($data) {
              if (strlen($data) < 8) {
                  return 0;
              }
              $length = intval(trim(substr($data, 0, 8)));
              if ($length <= 0) {
                  return -1;
              }
              return $length + 8;
          },
          'package_max_length'  => 2000000,  //协议最大长度
      ));
      
      $server->on('receive', function (Swoole\Server $server, $fd, $from_id, $data) {
          var_dump($data);
          echo "#{$server->worker_id}>> received length=" . strlen($data) . "\n";
      });
      
      $server->start();
    • C++长度解析函数

      ?> 在其他PHP扩展中,使用swoole_add_function注册长度函数到Swoole引擎中。

      !> C++长度函数调用时底层不会加锁,需要调用方自行保证线程安全性

      #include <string>
      #include <iostream>
      #include "swoole.h"
      
      using namespace std;
      
      int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length);
      
      void register_length_function(void)
      {
          swoole_add_function((char *) "test_get_length", (void *) test_get_length);
          return SW_OK;
      }
      
      int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length)
      {
          printf("cpp, size=%d\n", length);
          return 100;
      }

package_max_length

?> 设置最大数据包尺寸,单位为字节。【默认值:2M2 * 1024 * 1024,最小值为64K

?> 开启open_length_check/open_eof_check/open_eof_split/open_http_protocol/open_http2_protocol/open_websocket_protocol/open_mqtt_protocol等协议解析后。Swoole底层会进行数据包拼接。这时在数据包未收取完整时,所有数据都是保存在内存中的。
所以需要设定package_max_length,一个数据包最大允许占用的内存尺寸。如果同时有1万个TCP连接在发送数据,每个数据包2M,那么最极限的情况下,就会占用20G的内存空间。

  • 提示

    • open_length_check:当发现包长度超过package_max_length,将直接丢弃此数据,并关闭连接,不会占用任何内存;
    • open_eof_check:因为无法事先得知数据包长度,所以收到的数据还是会保存到内存中,持续增长。当发现内存占用已超过package_max_length时,将直接丢弃此数据,并关闭连接;
    • open_http_protocolGET请求最大允许8K,而且无法修改配置。POST请求会检测Content-Length,如果Content-Length超过package_max_length,将直接丢弃此数据,发送http 400错误,并关闭连接;
  • 注意

    !> 此参数不宜设置过大,否则会占用很大的内存

open_http_protocol

?> 启用Http协议处理。【默认值:false

?> 启用Http协议处理,Swoole\Http\Server会自动启用此选项。设置为false表示关闭Http协议处理。

open_mqtt_protocol

?> 启用mqtt协议。【默认值:false

?> 启用后会解析mqtt包头,worker进程onReceive每次会返回一个完整的mqtt数据包。

$server->set(array(
  'open_mqtt_protocol' => true
));

open_websocket_protocol

?> 启用websocket协议处理。【默认值:false

?> 启用websocket协议处理,Swoole\WebSocket\Server会自动启用此选项。设置为false表示关闭websocket协议处理。
设置open_websocket_protocol选项为true后,会自动设置open_http_protocol协议也为true

open_websocket_close_frame

?> 启用websocket协议中关闭帧。【默认值:false

?> (opcode0x08的帧)在onMessage回调中接收

?> 开启后,可在WebSocketServer中的onMessage回调中接收到客户端或服务端发送的关闭帧,开发者可自行对其进行处理。

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);

$server->set(array("open_websocket_close_frame" => true));

$server->on('open', function (Swoole\WebSocket\Server $server, $request) {});

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    if ($frame->opcode == 0x08) {
        echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n";
    } else {
        echo "Message received: {$frame->data}\n";
    }
});

$server->on('close', function ($ser, $fd) {});

$server->start();

open_tcp_nodelay

?> 启用open_tcp_nodelay【默认值:false

?> 开启后TCP连接发送数据时会关闭Nagle合并算法,立即发往对端TCP连接。在某些场景下,如命令行终端,敲一个命令就需要立马发到服务器,可以提升响应速度,请自行Google Nagle算法。

open_cpu_affinity

?> 启用CPU亲和性设置。 【默认 false 关闭】

?> 在多核的硬件平台中,启用此特性会将Swoolereactor线程/worker进程绑定到固定的一个核上。可以避免进程/线程的运行时在多个核之间互相切换,提高CPU Cache的命中率。

  • 提示

    • 使用taskset命令查看进程的CPU亲和设置:

      taskset -p 进程ID
      pid 24666's current affinity mask: f
      pid 24901's current affinity mask: 8

      ?> mask是一个掩码数字,按bit计算每bit对应一个CPU核,如果某一位为0表示绑定此核,进程会被调度到此CPU上,为0表示进程不会被调度到此CPU

      ?> 示例中pid24666的进程mask = f 表示未绑定到CPU,操作系统会将此进程调度到任意一个CPU核上。 pid24901的进程mask = 88转为二进制是 1000,表示此进程绑定在第4CPU核上。

cpu_affinity_ignore

?> IO密集型程序中,所有网络中断都是用CPU0来处理,如果网络IO很重,CPU0负载过高会导致网络中断无法及时处理,那网络收发包的能力就会下降。【默认值:】

?> 如果不设置此选项,swoole将会使用全部CPU核,底层根据reactor_id或worker_id与CPU核数取模来设置CPU绑定。
如果内核与网卡有多队列特性,网络中断会分布到多核,可以缓解网络中断的压力

array('cpu_affinity_ignore' => array(0, 1)) // 接受一个数组作为参数,array(0, 1) 表示不使用CPU0,CPU1,专门空出来处理网络中断。
  • 提示

    • 查看网络中断
[~]$ cat /proc/interrupts 
           CPU0       CPU1       CPU2       CPU3       
  0: 1383283707          0          0          0    IO-APIC-edge  timer
  1:          3          0          0          0    IO-APIC-edge  i8042
  3:         11          0          0          0    IO-APIC-edge  serial
  8:          1          0          0          0    IO-APIC-edge  rtc
  9:          0          0          0          0   IO-APIC-level  acpi
 12:          4          0          0          0    IO-APIC-edge  i8042
 14:         25          0          0          0    IO-APIC-edge  ide0
 82:         85          0          0          0   IO-APIC-level  uhci_hcd:usb5
 90:         96          0          0          0   IO-APIC-level  uhci_hcd:usb6
114:    1067499          0          0          0       PCI-MSI-X  cciss0
130:   96508322          0          0          0         PCI-MSI  eth0
138:     384295          0          0          0         PCI-MSI  eth1
169:          0          0          0          0   IO-APIC-level  ehci_hcd:usb1, uhci_hcd:usb2
177:          0          0          0          0   IO-APIC-level  uhci_hcd:usb3
185:          0          0          0          0   IO-APIC-level  uhci_hcd:usb4
NMI:      11370       6399       6845       6300 
LOC: 1383174675 1383278112 1383174810 1383277705 
ERR:          0
MIS:          0

eth0/eth1就是网络中断的次数,如果CPU0 - CPU3 是平均分布的,证明网卡有多队列特性。如果全部集中于某一个核,说明网络中断全部由此CPU进行处理,一旦此CPU超过100%,系统将无法处理网络请求。这时就需要使用 cpu_affinity_ignore 设置将此CPU空出,专门用于处理网络中断。

如图上的情况,应当设置 cpu_affinity_ignore => array(0)

?> 可以使用top指令 -> 输入 1,查看到每个核的使用率

  • 注意

    !> 此选项必须与open_cpu_affinity同时设置才会生效

tcp_defer_accept

?> 启用tcp_defer_accept特性【默认值:false 关闭】

?> 可以设置为一个数值,表示当一个TCP连接有数据发送时才触发accept

$server->set(array(
  'tcp_defer_accept' => 5
));
  • 提示

    • 启用tcp_defer_accept特性后,acceptonConnect对应的时间会发生变化。如果设置为5秒:

      • 客户端连接到服务器后不会立即触发accept
      • 5秒内客户端发送数据,此时会同时顺序触发accept/onConnect/onReceive
      • 5秒内客户端没有发送任何数据,此时会触发accept/onConnect

ssl_cert_file

?> 设置SSL隧道加密。【默认值:无】

?> 设置值为一个文件名字符串,指定cert证书和key私钥的路径。

  • 提示

    • PEMDER格式
    openssl x509 -in cert.crt -outform der -out cert.der
    • DERPEM格式
    openssl x509 -in cert.crt -inform der -outform pem -out cert.pem
  • 注意

    !> -https应用浏览器必须信任证书才能浏览网页;
    -wss应用中,发起WebSocket连接的页面必须使用 https
    -浏览器不信任SSL证书将无法使用 wss
    -文件必须为PEM格式,不支持DER格式,可使用openssl工具进行转换。

    !> 使用SSL必须在编译Swoole时加入--enable-openssl选项

    $server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
    $server->set(array(
        'ssl_cert_file' => __DIR__.'/config/ssl.crt',
        'ssl_key_file' => __DIR__.'/config/ssl.key',
    ));

ssl_method

?> 设置OpenSSL隧道加密的算法。【默认值:SWOOLE_SSLv23_METHOD

?> ServerClient使用的算法必须一致,否则SSL/TLS握手会失败,连接会被切断

$server->set(array(
    'ssl_method' => SWOOLE_SSLv3_CLIENT_METHOD,
));

ssl_ciphers

?> 设置 openssl 加密算法。【默认值:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH

$server->set(array(
    'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP',
));
  • 提示

    • ssl_ciphers 设置为空字符串时,由openssl自行选择加密算法

ssl_verify_peer

?> 服务SSL设置验证对端证书。【默认值:false

?> 默认关闭,即不验证客户端证书。若开启,必须同时设置 ssl_client_cert_file 选项

$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$server->set(array(
    'ssl_cert_file'         => __DIR__ . '/config/ssl.crt',
    'ssl_key_file'          => __DIR__ . '/config/ssl.key',
    'ssl_verify_peer'       => true,
    'ssl_allow_self_signed' => true,
    'ssl_client_cert_file'  => __DIR__ . '/config/client.crt',
));
  • 提示

    • ssl_verify_peer 开启验证对端证书功能,
    • ssl_allow_self_signed 允许自签名证书
    • ssl_client_cert_file 客户端正证书
  • 注意

    !> tcp服务若验证失败,会底层会主动关闭连接。

user

?> 设置Worker/TaskWorker子进程的所属用户。【默认值:执行脚本用户】

?> 服务器如果需要监听1024以下的端口,必须有root权限。但程序运行在root用户下,代码中一旦有漏洞,攻击者就可以以root的方式执行远程指令,风险很大。配置了user项之后,可以让主进程运行在root权限下,子进程运行在普通用户权限下。

$server->set(array(
  'user' => 'Apache'
));
  • 注意

    !> -仅在使用root用户启动时有效
    -使用user/group配置项将工作进程设置为普通用户后,将无法在工作进程调用shutdown/reload方法关闭或重启服务。只能使用root账户在shell终端执行kill命令。

group

?> 设置worker/task子进程的进程用户组。【默认值:执行脚本用户组】

?> 与user配置相同,此配置是修改进程所属用户组,提升服务器程序的安全性。

$server->set(array(
  'group' => 'www-data'
));
  • 注意

    !> 仅在使用root用户启动时有效

chroot

?> 重定向Worker进程的文件系统根目录。【默认值:无】

?> 此设置可以使进程对文件系统的读写与实际的操作系统文件系统隔离。提升安全性。

$server->set(array(
  'chroot' => '/data/server/'
));

pid_file

?> 设置 pid 文件地址。【默认值:无】

?> 在Server启动时自动将master进程的PID写入到文件,在Server关闭时自动删除PID文件。

$server->set(array(
    'pid_file' => __DIR__.'/server.pid',
));
  • 注意

    !> 使用时需要注意如果Server非正常结束,PID文件不会删除,需要使用Swoole\Process::kill($pid, 0)来侦测进程是否真的存在

buffer_output_size

?> 配置发送输出缓存区内存尺寸。【默认值:2M

$server->set([
    'buffer_output_size' => 32 * 1024 *1024, //必须为数字
]);
  • 提示

    • 单位为字节,默认为2M,如设置32 * 1024 *1024表示,单次Server->send最大允许发送32M字节的数据
    • 调用Server->sendHttp\Server->end/writeWebSocket\Server->push 等发送数据指令时,单次最大发送的数据不得超过buffer_output_size配置。
  • 注意

    !> -函数不应当调整过大,避免拥塞的数据过多,导致吃光机器内存
    -开启大量Worker进程时,将会占用worker_num * buffer_output_size字节的内存

此参数只针对SWOOLE_PROCESS模式生效,因PROCESS模式下worker进程的数据要发送给主进程再发送给客户端, 所以每个worker进程会主进程开辟一块缓冲区。参考

enable_unsafe_event

?> 启用onConnect/onClose事件。【默认值:false

?> Swoole在配置 dispatch_mode=1 或3后,因为系统无法保证onConnect/onReceive/onClose的顺序,默认关闭了onConnect/onClose事件;
如果应用程序需要onConnect/onClose事件,并且能接受顺序问题可能带来的安全风险,可以通过设置enable_unsafe_eventtrue,启用onConnect/onClose事件。

discard_timeout_request

?> 丢弃已关闭链接的数据请求。【默认值:true

?> Swoole在配置dispatch_mode=13后,系统无法保证onConnect/onReceive/onClose的顺序,因此可能会有一些请求数据在连接关闭后,才能到达Worker进程。

  • 提示

    • discard_timeout_request配置默认为true,表示如果worker进程收到了已关闭连接的数据请求,将自动丢弃。
    • discard_timeout_request如果设置为false,表示无论连接是否关闭Worker进程都会处理数据请求。

enable_reuse_port

?> 设置端口重用。【默认值:false

?> 启用端口重用后,可以重复启动监听同一个端口的 Server 程序

  • 提示

    • enable_reuse_port = true 打开端口重用
    • enable_reuse_port = false 关闭端口重用

!> 仅在Linux-3.9.0以上版本的内核可用 Swoole4.5以上版本可用

enable_delay_receive

?> 设置accept客户端连接后将不会自动加入EventLoop【默认值:false

?> 设置此选项为true后,accept客户端连接后将不会自动加入EventLoop,仅触发onConnect回调。worker进程可以调用 $server->confirm($fd)对连接进行确认,此时才会将fd加入EventLoop开始进行数据收发,也可以调用$server->close($fd)关闭此连接。

//开启enable_delay_receive选项
$server->set(array(
    'enable_delay_receive' => true,
));

$server->on("Connect", function ($server, $fd, $reactorId) {
    $server->after(2000, function() use ($server, $fd) {
        //确认连接,开始接收数据
        $server->confirm($fd);
    });
});

reload_async

?> 设置异步重启开关。【默认值:true

?> 设置异步重启开关。设置为true时,将启用异步安全重启特性,Worker进程会等待异步事件完成后再退出。详细信息请参见 如何正确的重启服务

?> reload_async 开启的主要目的是为了保证 服务重载时,协程 或 异步 任务能正常结束。

$server->set([
  'reload_async' => true
]);
  • 协程模式

    • 4.x版本中开启 enable_coroutine时,底层会额外增加一个协程数量的检测,当前无任何协程时进程才会退出,开启时即使reload_async => false也会强制打开reload_async

max_wait_time

?> 设置 Worker 进程收到停止服务通知后最大等待时间【默认值:3

?> 经常会碰到由于worker阻塞卡顿导致worker无法正常reload, 无法满足一些生产场景,例如发布代码热更新需要reload进程。所以,我们加入了进程重启超时时间的选项。详细信息请参见 如何正确的重启服务

  • 提示

    • 管理进程收到重启、关闭信号后或者达到max_request时,管理进程会重起该worker进程。分以下几个步骤:

      • 底层会增加一个(max_wait_time)秒的定时器,触发定时器后,检查进程是否依然存在,如果是,会强制杀掉,重新拉一个进程。
      • 需要在onWorkerStop回调里面做收尾工作,需要在max_wait_time秒内做完收尾。
      • 依次向目标进程发送SIGTERM信号,杀掉进程。
  • 注意

    !> v4.4.x以前默认为30

tcp_fastopen

?> 开启TCP快速握手特性。【默认值:false

?> 此项特性,可以提升TCP短连接的响应速度,在客户端完成握手的第三步,发送SYN包时携带数据。

$server->set([
  'tcp_fastopen' => true
]);
  • 提示

    • 此参数可以设置到监听端口上,想深入理解的同学可以查看google论文

request_slowlog_file

?> 开启请求慢日志。【默认值:false

!> 由于这个慢日志的方案只能在同步阻塞的进程里面生效,不能在协程环境用,而Swoole4默认就是开启协程的,除非关闭enable_coroutine,所以不要使用了,使用Swoole Tracker的阻塞检测工具。

?> 启用后Manager进程会设置一个时钟信号,定时侦测所有TaskWorker进程,一旦进程阻塞导致请求超过规定的时间,将自动打印进程的PHP函数调用栈。

?> 底层基于ptrace系统调用实现,某些系统可能关闭了ptrace,无法跟踪慢请求。请确认kernel.yama.ptrace_scope内核参数是否0

$server->set([
  'request_slowlog_file' => '/tmp/trace.log',
]);
  • 提示

    • 超时时间

      $server->set([
        'request_slowlog_timeout' => 2, // 设置请求超时时间为2秒
        'request_slowlog_file' => '/tmp/trace.log',
        'trace_event_worker' => true, //跟踪 Task 和 Worker 进程
      ]);
  • 注意

    !> 需要与trace_event_workerrequest_slowlog_timeout配置项配合使用。
    必须是具有可写权限的文件,否则创建文件失败底层会抛出致命错误;
    默认仅监听 Task进程,通过增加trace_event_worker => true来开启对Worker进程的跟踪

enable_coroutine

?> 开启异步风格服务器的协程支持【默认值:On

?> enable_coroutine 关闭时在事件回调函数中不再自动创建协程,如果不需要用协程关闭这个会提高一些性能 参考什么是Swoole协程

  • 配置方法

    • php.ini配置 swoole.enable_coroutine = 'Off' (可见 ini配置文档 )
    • $server->set(['enable_coroutine' => false]);优先级高于ini
  • enable_coroutine选项影响范围

    • onWorkerStart
    • onConnect
    • onOpen
    • onReceive
    • redis_onReceive
    • onPacket
    • onRequest
    • onMessage
    • onPipeMessage
    • onFinish
    • onClose
    • tick/after 定时器

!> 上述回调函数无论是否开启enable_coroutine都会自动创建所谓的协程容器,有了容器后就可以直接创建协程,不需要Co\run()

  • enable_coroutine设置为true时,底层自动在onRequest回调中创建协程,开发者无需自行使用go函数创建协程
  • enable_coroutine设置为false时,底层不会自动创建协程,开发者如果要使用协程,必须使用go自行创建协程,如果不需要使用协程特性,则处理方式与Swoole1.x是100%一致的
$server = new Swoole\Http\Server("127.0.0.1", 9501);

$server->set([
    //关闭内置协程
    'enable_coroutine' => false,
]);

$server->on("request", function ($request, $response) {
    if ($request->server['request_uri'] == '/coro') {
        go(function () use ($response) {
            co::sleep(0.2);
            $response->header("Content-Type", "text/plain");
            $response->end("Hello World\n");
        });
    } else {
        $response->header("Content-Type", "text/plain");
        $response->end("Hello World\n");
    }
});

$server->start();

max_coroutine

?> 设置当前工作进程最大协程数量。【默认值:3000

?> 超过max_coroutine底层将无法创建新的协程,底层会抛出错误,并直接关闭连接。

?> 在Server程序中实际最大可创建协程数量等于 worker_num * max_coroutine

$server->set(array(
    'max_coroutine' => 3000,
))

send_yield

?> 当发送数据时缓冲区内存不足时,直接在当前协程内yield,等待数据发送完成,缓存区清空时,自动resume当前协程,继续send数据。【默认值:在dispatch_mod 2/4时候可用,并默认开启】

  • Server/Client->send返回false并且错误码为SW_ERROR_OUTPUT_BUFFER_OVERFLOW时,不返回falsePHP层,而是yield挂起当前协程
  • Server/Client监听缓冲区是否清空的事件,在该事件触发后,缓存区内的数据已被发送完毕,这时resume对应的协程
  • 协程恢复后,继续调用Server/Client->send向缓存区内写入数据,这时因为缓存区已空,发送必然是成功的

改进前

for ($i = 0; $i < 100; $i++)
{
    //在缓存区塞满时会直接返回 false,并报错 output buffer overflow
    $server->send($fd, $data_2m);
}

改进后

for ($i = 0; $i < 100; $i++)
{
    //在缓存区塞满时会 yield 当前协程,发送完成后 resume 继续向下执行
    $server->send($fd, $data_2m);
}

!> 此项特性会改变底层的默认行为,可以手动关闭

$server->set([
    'send_yield' => false,
]);

hook_flags

?> 设置一键协程化Hook的函数范围。【默认值:不hook】

!> Swoole版本为 v4.5+4.4LTS 可用,详情参考一键协程化 >= 可用,详情参考一键协程化

$server->set([
    'hook_flags' => SWOOLE_HOOK_SLEEP,
]);