十五、Swoole 基础学习笔记 - Swoole 守护进程&信号&平滑重启

作者: 温新

分类: 【Swoole 系列】

阅读: 1389

时间: 2023-03-13 11:18:39

hi,我是温新,一名PHPer

文章基于 Swoole 5.0.1 版本编写。

学习目标:守护进程启动服务后的相关操作,如重启等

说明:本篇文章结合官方文档编写及参考网络资料编写,虽非全部原创,但也是结合了自己的理解,若转载请附带本文 URL,编写不易,持续编写更不易,谢谢!

守护进程

守护进程(daemon)是一种长期生存的进程,它不受终端控制,可以在后台运行。如 ngxin、php-fpm等一般都是作为守护进程在后台提供服务。

守护进程可以在后台运行,但也存在缺点。启用守护进程后,server 内所有的标准输出都会被丢弃,这也就是说,进程运行过程中出现的异常也将无法追踪。为此可以配置 log_file,配置后 swoole 在运行时,会把所有的标准输出记录到该文件。

配置守护进程

$server->set([
    // 启用守护进程
    'daemonize' => true,
    'log_file'  => __DIR__ . '/server.log'
]);

平滑重启

Server 启动后,由于程序常驻内存,因此,当代码有修改时,需要使用 Ctrl + ckill 来终端程序允许,然后再重新启动使之修改的代码生效。

若是处于生产环境,当有新功能增加时,需要中断服务然后重新启动,想想这后果是非常严重的。

在 Swoole 中可以使用平滑重启,而平滑重启是对 Server 发送特定的信号。那么什么是信号?

什么是信号?

信号以 SIG 开头,如常用的 Ctrl + c 其实就是向服务发送 SIGINT 的信号,也就是终端服务。在 Swoole 中,可以向主进程发送各种不同的信号,主进程会根据收到的信号类型做出不同的处理。常用信号如下:

  • 1、SIGTERM,一种优雅的终止信号,进程执行完当前程序后再中断。使用:kill -SIGTERM master_pid
  • 2、SIGUSR1,重启所有 Worker 进程。使用:kill -USR1 master_pid
  • 3、SIGUSR2,重启所有 Task 进程。使用:kill -USR2 master_pid

当 USR1 信号被发送给 Master 进程后,Master 进程会将同样的信号通过 Manager 进程转发给 Worker 进程,收到该信号的 Worker 进程会在处理完正在执行的逻辑后,释放进程内存,关闭自己,然后由 Manager 进程重启一个新的 Worker 进程。新的 Worker 进程会占用新的内存空间。

平滑重启的原理:当主进程收到 USR1 信号时,主进程会向一个子进程发送安全退出的信号。安全退出指的是等到子进程处理完手头的工作后再将其杀死,可以理解为过河拆桥。

到这里可以知道,平滑重启本质是让旧的子进程逐一退出然后再创建新的进程。为了在平滑重启时不影响用户,需要保持业务进程是无状态的。

注意:

1、在 Swoole 中,重启只能针对 Worker 进程,写在 master 进程和 manager 进程中的更新代码不会生效,也就是,只有人 onWorkerStart 回调之后加载的文件,重启才能有意义。在 Worker 进程启动前就加载到内存中的文件,若要生效,就只能关闭 Server再重启;

2、直接写在 Worker 代码中的逻辑不会生效,即使是发送了信号。需要通过 include 方式引入相关代码才会生效。

案例演示

修改代码未生效

Test 类,用于处理服务端接收的数据

<?php
// 14-test.php
class Test
{
	public function run($data)
	{
		echo  $data . PHP_EOL;
	}
}

服务端代码

<?php
// 14-swoole-server.php
    
// 引入其他文件
require_once '14-test.php';

$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS);

$server->on('Receive', function ($server, $fd, $reactorId, $data) {
    // 发送消息
    $test = new Test;
    $test->run($data);
});

$server->start();

客户端使用 telnet 进程连接。

向服务端发送消息,可以看到,服务端已经接收到了客户端发送的数据。

$php 14-swoole-server-1.php 
hello

1)修改 test 类方法:

<?php

class Test
{
	public function run($data)
	{
		echo 'hi:' . $data . PHP_EOL;
	}
}

2)现在我们找到服务端进程号并平滑重启:

$netstat -nltp | grep 9501
tcp        0      0 0.0.0.0:9501            0.0.0.0:*               LISTEN      19495/php  

# 平滑重启
$kill -USR1 19495

3)再次连接服务端并向服务端发送数据,发现修改后的代码未生效。

未生效的原因是:新的代码只有在 onWorkerStart 之后加载进来才会生效。

修改代码,平滑重启使之生效

<?php
// 14-swoole-server-1.php

$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS);

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

$server->on('Receive', function ($server, $fd, $reactorId, $data) {
    $test = new Test;
    $test->run($data);
});

$server->on('Workerstart', function($server, $workerId) 
	// workerStart 之后引入,平滑重启会生效
    require_once '14-test.php';
});

$server->start();

这后修改后,就实现了热重启。

本篇文章到此结束,我是温新,下篇文章继续学习 Swoole。

请登录后再评论