swoole 进程守护 日志保存

前边我已经学会了 Swoole 的 Server 和 Client,也用他们做了不同压力测试、并发测试等等。但是终究离应用到实际项目还差一步,因为没有开启进程守护,只要我的终端关掉了,程序也就中断执行了。

其实在写这篇之前,不依赖 Swoole 为我们提供的方法,如果 Linux 算熟悉的话,自己也能想到办法:

1
$ php server.php > server_log.log &

进程守护 日志保存

但是既然 Swoole 给提供了,那我们就开看一下怎么用的吧。
daemonize设置为true时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。如果不启用守护进程,当ssh终端退出后,程序将被终止运行。

  • 启用守护进程后,标准输入和输出会被重定向到 log_file
  • 如果未设置log_file,将重定向到 /dev/null,所有打印屏幕的信息都会被丢弃

代码实现,只要将我们之前讲过的 server 配置添加daemonizelog_file即可:

1
2
3
4
5
$serv->set([
'worker_num' => 1,
'daemonize' => true,
'log_file' => __DIR__ . '/server.log'
]);

设置好以后,确认是够已经成功进行进程守护,可使用命令查看:

1
2
3
4
5
$ ps aux | grep server.php
yangyiyuan 92904 0.0 0.0 2491660 2280 ?? S 10:54上午 0:00.00 php server.php
yangyiyuan 92861 0.0 0.0 2491052 1596 ?? S 10:54上午 0:00.00 php server.php
yangyiyuan 92857 0.0 0.0 2492280 1860 ?? Ss 10:54上午 0:00.27 php server.php
yangyiyuan 98783 0.0 0.0 2423392 600 s001 R+ 1:52下午 0:00.00 grep server.php

此时执行我们的客户端文件,将会有日志输出到server.log文件中。

重启进程

一台繁忙的后端服务器随时都在处理请求,如果管理员通过kill进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止。
这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。
Swoole提供了柔性终止/重启的机制,管理员只需要向SwooleServer发送特定的信号,Server的worker进程可以安全的结束。

  • SIGTERM: 向主进程/管理进程发送此信号服务器将安全终止,在PHP代码中可以调用$serv->shutdown()完成此操作
  • SIGUSR1: 向主进程/管理进程发送SIGUSR1信号,将平稳地restart所有worker进程,在PHP代码中可以调用$serv->reload()完成此操作
  • swoole的reload有保护机制,当一次reload正在进行时,收到新的重启信号会丢弃
  • 如果设置了user/group,Worker进程可能没有权限向master进程发送信息,这种情况下必须使用root账户,在shell中执行kill指令进行重启
  • reload指令对addProcess添加的用户进程无效

终端重启所有worker进程:

1
$ kill -USR1 主进程PID

终端仅重启task进程

1
$ kill -USR2 主进程PID

注意事项:

平滑重启只对onWorkerStart或onReceive等在Worker进程中include/require的PHP文件有效,Server启动前就已经include/require的PHP文件,不能通过平滑重启重新加载
对于Server的配置即$serv->set()中传入的参数设置,必须关闭/重启整个Server才可以重新加载
Server可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有worker

Reload有效范围:
Reload操作只能重新载入Worker进程启动后加载的PHP文件,建议使用get_included_files函数来列出哪些文件是在WorkerStart之前就加载的PHP文件,在此列表中的PHP文件,即使进行了reload操作也无法重新载入。比如要关闭服务器重新启动才能生效。
WorkerStart之前就加载的PHP文件:

1
2
3
$serv->on('WorkerStart', function($serv, $workerId) {
var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload
});

重启无效:

1
2
3
4
require_once './Test.php';
class Server{
}

重启有效:

1
2
3
4
$serv->on('WorkerStart', function($serv, $workerId) {
require_once("Test.php"); //此处 Test.php重启有效
$this->_test = new Test;
});

坚持原创技术分享,您的支持将鼓励我继续创作!