呕心搜集总结的15个“swoole”常见问题(二)

之前已经说了前面八个问题,现在接着往下分享,没有看到之前的文章的可以点击这里哦~

九、Swoole如何正确的重启服务

在我们修改了 PHP 代码后经常需要重启服务让代码生效,一台繁忙的后端服务器随时都在处理请求,如果管理员通过 kill 进程方式来终止 / 重启服务器程序,可能导致刚好代码执行到一半终止,没法保证整个业务逻辑的完整性。

Swoole 提供了柔性终止 / 重启的机制,管理员只需要向 Server 发送特定的信号或者调用 reload 方法,工作进程就可以结束,并重新拉起。

但有几点要注意:

首先要注意新修改的代码必须要在 OnWorkerStart 事件中重新载入才会生效,比如某个类在 OnWorkerStart 之前就通过 composer 的 autoload 载入了就是不可以的。

其次 reload 还要配合这两个参数 max_wait_time 和 reload_async,设置了这两个参数之后就能实现异步安全重启。

如果没有此特性,Worker 进程收到重启信号或达到 max_request 时,会立即停止服务,这时 Worker 进程内可能仍然有事件监听,这些异步任务将会被丢弃。设置上述参数后会先创建新的 Worker,旧的 Worker 在完成所有事件之后自行退出,即 reload_async。

如果旧的 Worker 一直不退出,底层还增加了一个定时器,在约定的时间 ( max_wait_time 秒) 内旧的 Worker 没有退出,底层会强行终止。

示例:

<?php
$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_PROCESS);
$serv->set(array(
    'worker_num' => 1,
    'max_wait_time' => 60,
    'reload_async' => true,
));
$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) {

    echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n";

    Swoole\Timer::tick(5000, function () {
        echo 'tick';
    });
});

$serv->start();

例如上面的代码 如果没有 reload_async 那么 onReceive 中创建的定时器将丢失,没有机会处理定时器中的回调函数。

进程退出事件

为了支持异步重启特性,底层新增了一个 onWorkerExit 事件,当旧的 Worker 即将退出时,会触发 onWorkerExit 事件,在此事件回调函数中,应用层可以尝试清理某些长连接 Socket,直到事件循环中没有 fd 或者达到了 max_wait_time 退出进程。

$serv->on('WorkerExit', function (Swoole\Server $serv, $worker_id) {
    $redisState = $serv->redis->getState();
    if ($redisState == Swoole\Redis::STATE_READY or $redisState == Swoole\Redis::STATE_SUBSCRIBE)
    {
        $serv->redis->close();
    }
});

同时我们在 Swoole Plus 中增加了检测文件变化的功能,可以不用手动 reload 或者发送信号,文件变更自动重启 worker。

十、为什么不要send完后立即close就是不安全的

send 完后立即 close 就是不安全的,无论是服务器端还是客户端。

send 操作成功只是表示数据成功地写入到操作系统 socket 缓存区,不代表对端真的接收到了数据。究竟操作系统有没有发送成功,对方服务器是否收到,服务器端程序是否处理,都没办法确切保证。

close 后的逻辑请看下面的 linger 设置相关

这个逻辑和电话沟通是一个道理,A 告诉 B 一个事情,A 说完了就挂掉电话。那么 B 听到没有,A 是不知道的。如果 A 说完事情,B 说好,然后 B 挂掉电话,就绝对是安全的。

linger 设置

一个 socket 在 close 时,如果发现缓冲区仍然有数据,操作系统底层会根据 linger 设置决定如何处理

struct linger
{
     int l_onoff;
     int l_linger;
};
  • l_onoff = 0,close 时立刻返回,底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出。
  • l_onoff != 0,l_linger = 0,close 时会立刻返回,但不会发送未发送完成的数据,而是通过一个 RST 包强制的关闭 socket 描述符,也就是强制的退出。
  • l_onoff !=0,l_linger > 0, closes 时不会立刻返回,内核会延迟一段时间,这个时间就由 l_linger 的值来决定。如果超时时间到达之前,发送完未发送的数据 (包括 FIN 包) 并得到另一端的确认,close 会返回正确,socket 描述符优雅性退出。否则 close 会直接返回错误值,未发送数据丢失,socket 描述符被强制性退出。如果 socket 描述符被设置为非堵塞型,则 close 会直接返回值。

十一、连接已关闭问题

如以下提示

NOTICE    swFactoryProcess_finish (ERRNO 1004): send 165 byte failed, because connection[fd=123] is closed
NOTICE    swFactoryProcess_finish (ERROR 1005): connection[fd=123] does not exists

服务端响应时,客户端已经切断了连接导致

常见于:

  • 浏览器疯狂刷新页面 (还没加载完就刷掉了)
  • ab 压测到一半取消
  • wrk 基于时间的压测 (时间到了未完成的请求会被取消)
    以上几种情况均属于正常现象,可以忽略,所以该错误的级别是 NOTICE

如由于其它情况无缘无故出现大量连接断开时,才需要注意

十二、学习Swoole需要掌握哪些基础知识

1、多进程/多线程

  • 了解 Linux 操作系统进程和线程的概念
  • 了解 Linux 进程 / 线程切换调度的基本知识
  • 了解进程间通信的基本知识,如管道、UnixSocket、消息队列、共享内存

2、SOCKET

  • 了解 SOCKET 的基本操作如 accept/connect、send/recv、close、listen、bind
  • 了解 SOCKET 的接收缓存区、发送缓存区、阻塞 / 非阻塞、超时等概念

3、IO复用

  • 了解 select/poll/epoll
  • 了解基于 select/epoll 实现的事件循环,Reactor 模型
  • 了解可读事件、可写事件

4、TCP/IP网络协议

  • 了解 TCP/IP 协议
  • 了解 TCP、UDP 传输协议

5、调试工具

  • 使用 gdb 调试 Linux 程序
  • 使用 strace 跟踪进程的系统调用
  • 使用 tcpdump 跟踪网络通信过程
  • 其他 Linux 系统工具,如 ps、lsof、top、vmstat、netstat、sar、ss 等

十三、是否可以共用1个Redis或MySQL连接

绝对不可以。必须每个进程单独创建 Redis、MySQL、PDO 连接,其他的存储客户端同样也是如此。原因是如果共用 1 个连接,那么返回的结果无法保证被哪个进程处理,持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。

所以在多个进程之间,一定不能共用连接

  • 在 Swoole\Server 中,应当在 onWorkerStart 中创建连接对象
  • 在 Swoole\Process 中,应当在 Swoole\Process->start 后,子进程的回调函数中创建连接对象
  • 此问题所述信息对使用 pcntl_fork 的程序同样有效

示例:

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

//必须在onWorkerStart回调中创建redis/mysql连接
$server->on('workerstart', function($server, $id) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $server->redis = $redis;
});

$server->on('receive', function (Swoole\Server $server, $fd, $from_id, $data) {    
    $value = $server->redis->get("key");
    $server->send($fd, "Swoole: ".$value);
});

$server->start();

十四、Call to undefined function Co\Run()

本文档中的大部分示例都使用了 Co\run() 来创建一个协程容器,了解什么是协程容器

如果遇到如下错误:

PHP Fatal error:  Uncaught Error: Call to undefined function Co\Run()

PHP Fatal error:  Uncaught Error: Call to undefined function go()

说明你的 Swoole 扩展版本小于 v4.4.0 或者手动关闭了协程短名称,提供三种解决方法

  • 如果是版本过低,则请升级扩展版本至 >= v4.4.0 或使用 go 关键字替换 Co\Run 来创建协程;
  • 如果是关闭了协程短名称,则请打开协程短名称;
  • 使用 Coroutine::create 方法替换 Co\Run 或 go 来创建协程;

十五、Resource temporarily unavailable[11]

客户端 swoole_client 在 recv 时报

swoole_client::recv(): recv() failed. Error: Resource temporarily unavailable [11]

这个错误表示,服务器端在规定的时间内没有返回数据,接收超时了。

  • 可以通过 tcpdump 查看网络通信过程,检查服务器是否发送了数据
  • 服务器的 $serv->send 函数需要检测是否返回了 true
  • 外网通信时,耗时较多需要调大 swoole_client 的超时时间

进阶PHP月薪30k>>>架构师成长路线【视频、面试文档免费获取】

我的官方群点击此处加入群聊【PHP高级学习交流群953224940】,一起学习,相互讨论。群内已经有管理将知识体系整理好(源码,学习视频等资料),欢迎加群免费领取。

这套精品PHP教程绝不是市场上的那些妖艳贱货可比,作为web开发的佼佼者PHP并不逊色其他语言,加上Swoole后更加是如虎添翼!进军通信 、物联网行业开发百度地图、百度订单中心、虎牙、战旗TV等!寒冬裁员期过后正是各大企业扩大招人的时期,现在市场初级程序员泛滥,进阶中高级程序员绝对是各大企业急需的人才,这套学习教程适合那些1-5年以内的PHP开发者正处于瓶颈期,想要突破自己进阶中高级、架构师!名额有限,先到先得!

部分资料截图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

还有限时精品福利:

★腾讯高级PHP工程师笔试题目

★亿级PV高并发场景订单的处理

★laravel开发天猫商城组件服务

★战旗TV视频直播的架构项目实战

扫描下面二维码领取
正在跳转(二维码自动跳转)

对PHP后端技术,对PHP架构技术感兴趣的朋友,我的的官方群953224940,一起学习,相互讨论。

群内已经有管理将知识体系整理好(源码,学习视频等资料),欢迎免费领取。

本课程深度对标腾讯T3-T4标准,贴身打造学习计划为web开发人员进阶中高级、架构师提升技术,为自己增值涨薪!加入BAT特训营还可以获得内推大厂名额以及GO语言学习权限!!!

猜你喜欢

转载自blog.csdn.net/weixin_49163826/article/details/107639787