0

0

浅析Swoole server

coldplay.xixi

coldplay.xixi

发布时间:2021-03-11 09:52:08

|

2126人浏览过

|

来源于CSDN

转载

浅析Swoole server

一. 基础知识

1.1 Swoole

Swoole是面向生产环境的php异步网络通信引擎, php开发人员可以利用Swoole开发出高性能的server服务。Swoole的server部分, 内容很多, 也涉及很多的知识点, 本文仅对其server进行简单的概述, 具体的实现细节在后续的文章中再进行详细介绍。

 推荐(免费):swoole

1.2 网络编程

1. 网络通信是指在一台(或者多台)机器上启动一个(或者多个)进程, 监听一个(或者多个)端口, 按照某种协议(可以是标准协议http, dns; 也可以是自行定义的协议)与客户端交换信息。

2. 目前的网络编程多是在tcp, udp或者更上层的协议之上进行编程。Swoole的server部分是基于tcp以及udp协议的。

3. 利用udp进行编程较为简单, 本文主要介绍tcp协议之上的网络编程

4. TCP网络编程主要涉及4种事件

 连接建立: 主要是指客户端发起连接(connect)以及服务端接受连接(accept)

 消息到达: 服务端接受到客户端发送的数据,该事件是TCP网络编程最重要的事件,服务端对于该类事件进行处理时, 可以采用阻塞式或者非阻塞式,除此之外, 服务端还需要考虑分包, 应用层缓冲区等问题

 消息发送成功: 发送成功是指应用层将数据成功发送到内核的套接字发送缓冲区中,并不是指客户端成功接受数据。对于低流量的服务而言,数据通常一次性即可发送完,并不需要关心此类事件。如果一次性不能将全部数据发送到内核缓冲区,则需要关心消息是否成功发送(阻塞式编程在系统调用(write, writev, send等)返回后即是发送成功, 非阻塞式编程则需要考虑实际写入的数据是否与预期一致)

 连接断开: 需要考虑客户端断开连接(read返回0)以及服务端断开连接(close, shutdown)

5. tcp建立连接的过程如下图

● 图中, ACK、SYN表示标志位, seq、ack为tcp包的序号以及确认序号

6. tcp断开连接的过程如下图

 

● 上图考虑的是客户端主动断开连接的情况, 服务端主动断开连接也类似

● 图中, FIN、ACK表示标志位, seq、ack为tcp包的序号以及确认序号

 

1.3 进程间通信

1. 进程之间的通信有无名管道(pipe), 有名管道(fifo), 信号(signal), 信号量(semaphore), 套接字(socket), 共享内存(shared memory)等方式

2. Swoole中采用unix域套接字(套接字的一种)用于多进程之间的通信(指Swoole内部进程之间)

 

1.4 socketpair

1. socketpair用于创建一个套接字对, 类似于pipe, 不同的是pipe是单向通信, 双向通信需要创建两次, socketpair调用一次即可实现双向通信, 除此之外, 由于使用的是套接字, 还可以定义数据交换的方式

2. socketpair系统调用

  • 调用成功后sv[0], sv[1]分别存储一个文件描述符
  • 向sv[0]中写入, 可以从sv[1]中读取
  • 向sv[1]中写入, 可以从sv[0]中读取
  • 进程调用socketpair后, fork子进程, 子进程会默认继承sv[0], sv[1]这两个文件描述符, 进而可以实现父子进程间通信。例如, 父进程向sv[0]中写入, 子进程从sv[1]中读取; 子进程向sv[1]中写入, 父进程从sv[0]中读取

 

1.5 守护进程(daemon)

1. 守护进程是一种特殊的后台进程, 它脱离于终端, 用于周期性的执行某种任务

2. 进程组

  • 每个进程都属于一个进程组
  • 每个进程组都有一个进程组号, 也就是该组组长的进程号(PID)
  • 一个进程只能为自己或者其子进程设置进程组号

3. 会话

  • 一个会话可以包含多个进程组, 这些进程组中最多只能有一个前台进程组(也可以没有), 其余为后台进程组
  • 一个会话最多只能有一个控制终端
  • 用户通过终端登录或者网络登录, 会创建一个新的会话
  • 进程调用系统调用setsid可以创建一个新的会话, 调用setsid的进程不能是某个进程组的组长。setsid调用完成后, 该进程成为这个会话的首进程(领头进程), 同时变成一个新的进程组的组长, 如果该进程之前有控制终端, 该进程与终端的联系也被断开

4. 创建守护进程的方式

  • fork子进程后, 父进程退出, 子进程执行setsid, 子进程即可成为守护进程。这种方式下, 子进程是会话的领头进程, 可以重新打开终端, 此时可以再次fork, fork产生的子进程无法再打开终端(只有会话的领头进程才能打开终端)。第二次fork并不是必须的, 只是为了防止子进程再次打开终端
  • linux提供了daemon函数(该函数并不是系统调用, 为库函数)用于创建守护进程

1.6 Swoole tcp server示例

  • 上述代码在cli模式下执行时, 经过词法分析, 语法分析生成opcode, 进而交由zend虚拟机执行
  • zend虚拟机在执行到$serv->start()时, 启动Swoole server
  • 上述代码中设置的事件回调是在worker进程中执行, 后文会详细介绍Swoole server模型

 

二. Swoole server


2.1 base模式

eSiteGroup站群管理系统1.0.4
eSiteGroup站群管理系统1.0.4

eSiteGroup站群管理系统是基于eFramework低代码开发平台构建,是一款高度灵活、可扩展的智能化站群管理解决方案,全面支持SQL Server、SQLite、MySQL、Oracle等主流数据库,适配企业级高并发、轻量级本地化、云端分布式等多种部署场景。通过可视化建模与模块化设计,系统可实现多站点的快速搭建、跨平台协同管理及数据智能分析,满足政府、企业、教育机构等组织对多站点统一管控的

下载

1. 说明

  • base模式采用多进程模型, 这种模型与nginx一致, 每个进程只有一个线程, 主进程负责管理工作进程, 工作进程负责监听端口, 接受连接, 处理请求以及关闭连接
  • 多个进程同时监听端口, 会有惊群问题, linux 3.9之前版本的内核, Swoole没有解决惊群问题
  • linux 内核3.9及其后续版本提供了新的套接字参数SO_REUSEPORT, 该参数允许多个进程绑定到同一个端口, 内核在接受到新的连接请求时, 会唤醒其中一个进行处理, 内核层面也会做负载均衡, 可以解决上述的惊群问题, Swoole也已经加入了这个参数
  • base模式下, reactor_number参数并没有实际作用
  • 如果worker进程数设置为1, 则不会fork出worker进程, 主进程直接处理请求, 这种模式适合调试

2. 启动过程

  • php代码执行到$serv->start()时,主进程进入int swServer_start(swServer *serv)函数, 该函数负责启动server
  • 在函数swServer_start中会调用swReactorProcess_start, 这个函数会fork出多个worker进程
  • 主进程和worker进程各自进入自己的事件循环, 处理各类事件

2.2 process模式

1. 说明

  • 这种模式为多进程多线程, 有主进程, manager进程, worker进程, task_worker进程
  • 主进程下有多个线程, 主线程负责接受连接, 之后交给react线程处理请求。 react线程负责接收数据包, 并将数据转发给worker进程进行处理, 之后处理worker进程返回的数据
  • manager进程, 该进程为单线程, 主要负责管理worker进程, 类似于nginx中的主进程, 当worker进程异常退出时, manager进程负责重新fork出一个worker进程
  • worker进程, 该进程为单线程, 负责具体处理请求
  • task_worker进程, 用于处理比较耗时的任务, 默认不开启
  • worker进程与主进程中的react线程使用域套接字进行通信, worker进程之间不进行通信

2. 启动过程

  • Swoole server启动入口: swServer_start函数

  • 如果设置了daemon模式, 在必要的参数检查完后, 先将自己变为守护进程再fork manager进程, 进而创建reactor线程
  • 主进程先fork出manager进程, manager进程负责fork出worker进程以及task_worker进程。worker进程之后进入int swWorker_loop
    (swServer *serv, int worker_id), 也就是进入自己的事件循环, task_worker也是一样, 进入自己的事件循环

主进程pthread_create出react线程, 主线程和react线程各自进入自己的事件循环, reactor线程执行static int swRea-torThread_loop (swThreadParam *param), 等待处理事件

3. 结构图

  • Swoole process模式结构如下图所示,

上图并没有考虑task_worker进程, 在默认情况下, task_worker进程数为0

 

三. 请求处理流程(process模式)


3.1 reactor线程与worker进程之间的通信

1. Swoole master进程与worker进程之间的通信如下图所示

  • Swoole使用SOCK_DGRAM, 而不是SOCK_STREAM, 这里是因为每个reactor线程负责处理多个请求, reactor接收到请求后会将信息转发给worker进程, 由worker进程负责处理,如果使用SOCK_STREAM, worker进程无法对tcp进行分包, 进而处理请求
  • swFactoryProcess_start函数中会根据worker进程数创建对应个数的套接字对, 用于reactor线程与worker进程通信(详见swPipeUnsock_create函数)

2. 假设reactor线程有2个, worker进程有3个, 则reactor与worker之间的通信如下图所示

  • 每个reactor线程负责监听几个worker进程, 每个worker进程只有一个reactor线程监听(reactor_num
  • reactor线程收到某个worker进程的数据后会进行处理, 值得注意的是, 这个reactor线程可能并不是发送请求的那个reactor线程。

3. reactor线程与worker进程通信的数据包

3.2 请求处理

1. master进程中的主线程负责监听端口(listen), 接受连接(accept, 产生一个fd), 接受连接后将请求分配给reactor线程, 默认通过fd % reactor_number进行分配, 之后通过epoll_ctl将fd加入到对应reactor线程中, 刚加入时监听写事件, 因为新接受连接创建的套接字写缓冲区为空, 故而一定可写, 会被立刻触发, 进而reactor线程进行一些初始化操作

  • 存在多个线程同时操作一个epollfd(通过系统调用epoll_create创建)的情况
  • 多个线程同时调用epoll_ctl是线程安全的(对应一个epollfd), 一个线程正在执行, 其他线程会被阻塞(因为需要同时操作epoll底层的红黑树)
  • 多个线程同时调用epoll_wait也是线程安全的, 但是一个事件可能会被多个线程同时接收到, 实际中不建议多个线程同时epoll_wait一个epollfd。Swoole中也是不存在这种情况的, Swoole中每个reactor线程都有自己的epollfd
  • 一个线程调用epoll_wait, 一个线程调用epoll_ctl, 根据man手册, 如果epoll_ctl新加入的fd已经准备好, 会使得执行epoll_wait的线程变成非阻塞状态(可以通过man epoll_wait查看相关内容)

2. reactor线程中fd的写事件被触发, reactor线程负责处理, 发现是首次加入, 没有数据可写, 则会开启读事件监听, 准备接受客户端发送的数据

3. reactor线程读取到用户的请求数据, 一个请求的数据接收完后, 将数据转发给worker进程, 默认是通过fd % worker_number进行分配

  • reactor发送给worker进程的数据包, 会包含一个头部, 头部中记录了reactor的信息
  • 如果发送的数据过大, 则需要将数据进行分片, 限于篇幅, 数据分片, 后续再进行详细讲述
  • 可能存在多个reactor线程同时向同一个worker进程发送数据的情况, 故而Swoole采用SOCK_DGRAM模式与worker进程进行通信, 通过每个数据包的包头, worker进程可以区分出是由哪个reactor线程发送的数据, 也可以知道是哪个请求

4. worker进程收到reactor发送的数据包后, 进行处理, 处理完成后, 将请求结果发送给主进程

  • worker进程发送给主进程的数据包, 也会包含一个头部, 当reactor线程收到数据包后, 能够知道对应的reactor线程, 请求的fd等信息

5. 主进程收到worker进程发送的数据包, 这个会触发某个reactor线程进行处理

  • 这个reactor线程并不一定是之前发送请求给worker进程的那个reactor线程
  • 主进程的每个reactor线程都负责监听worker进程发送的数据包, 每个worker发送的数据包只会由一个reactor线程进行监听, 故而只会触发一个reactor线程

6. reactor线程处理worker进程发送的请求处理结果, 如果是直接发送数据给客户端, 则可以直接发送, 如果需要改变这个这个连接的监听状态(例如close), 则需要先找到监听这个连接的reactor线程, 进而改变这个连接的监听状态(通过调用epoll_ctl)

  • reactor处理线程与reactor监听线程可能并不是同一个线程
  • reactor监听线程负责监听客户端发送的数据, 进而转发给worker进程
  • reactor处理线程负责监听worker进程发送给主进程的数据, 进而将数据发送给客户端

 

四. gdb调试

4.1 process模式启动

4.2 base模式启动

五. 总结及思考

1. 本文主要介绍了Swoole server的两种模式: base模式、process模式, 详细讲解了两种模式的网络编程模型, 并重点介绍了process模式下, 进程间通信的方式、请求的处理流程等

2. process模式下, 为什么不直接在主进程中创建多个线程, 由线程直接进行处理请求(可以避免进程间通信的开销), 而是创建出manager进程, 再由manager进程创建出worker进程, 由worker进程处理请求?

  • 个人觉得可能是php对多线程的支持不是很友好, phper大都也只是进行单线程编程
  • ZendVM 提供的 TSRM 虽然也是支持多线程环境,但实际上这是一个 按线程隔离内存的方案, 多线程并没有意义

3. process模式下, 主进程中的每个reactor线程都可以同时处理多个请求, 多个请求是并发处理的, 我们从2个维度看

  • 从主进程的角度看, 主进程同时处理多个请求, 当一个请求包全部接收完后, 转发给worker进程进行处理
  • 从某个worker进程的角度看, 这个worker进程收到的请求是串行的, 默认情况下, worker进程也是串行处理请求, 如果单个请求阻塞(Swoole的worker进程会回调phper写的事件处理函数, 该函数可能阻塞), 后续的请求也无法处理, 这个就是排头阻塞问题, 这种情况下可以使用Swoole的协程, 通过协程的调度, 单个请求阻塞时, worker进程可以继续处理其他请求

4. 使用Swoole创建tcp server时, 由于tcp是字节流的协议, 需要分包, 而Swoole在不清楚客户端与服务端通信协议的情况下, 无法进行分包, process模式下, reactor交给worker进程的数据也只能是字节流的, 需要用户自行处理。当然, 一般情况也不需要自行构建协议, 使用tcp server, Swoole已经支持Http, Https等协议

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
nginx 重启
nginx 重启

nginx重启对于网站的运维来说是非常重要的,根据不同的需求,可以选择简单重启、平滑重启或定时重启等方式。本专题为大家提供nginx重启的相关的文章、下载、课程内容,供大家免费下载体验。

245

2023.07.27

nginx 配置详解
nginx 配置详解

Nginx的配置是指设置和调整Nginx服务器的行为和功能的过程。通过配置文件,可以定义虚拟主机、HTTP请求处理、反向代理、缓存和负载均衡等功能。Nginx的配置语法简洁而强大,允许管理员根据自己的需要进行灵活的调整。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

521

2023.08.04

nginx配置详解
nginx配置详解

NGINX与其他服务类似,因为它具有以特定格式编写的基于文本的配置文件。本专题为大家提供nginx配置相关的文章,大家可以免费学习。

588

2023.08.04

tomcat和nginx有哪些区别
tomcat和nginx有哪些区别

tomcat和nginx的区别:1、应用领域;2、性能;3、功能;4、配置;5、安全性;6、扩展性;7、部署复杂性;8、社区支持;9、成本;10、日志管理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

244

2024.02.23

nginx报404怎么解决
nginx报404怎么解决

当访问 nginx 网页服务器时遇到 404 错误,表明服务器无法找到请求资源,可以通过以下步骤解决:1. 检查文件是否存在且路径正确;2. 检查文件权限并更改为 644 或 755;3. 检查 nginx 配置,确保根目录设置正确、没有冲突配置等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

651

2024.07.09

Nginx报404错误解决方法
Nginx报404错误解决方法

解决方法:只需要加上这段配置:try_files $uri $uri/ /index.html;即可。想了解更多Nginx的相关内容,可以阅读本专题下面的文章。

3613

2024.08.07

nginx部署php项目教程汇总
nginx部署php项目教程汇总

本专题整合了nginx部署php项目教程汇总,阅读专题下面的文章了解更多详细内容。

53

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

70

2026.01.13

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

4

2026.03.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 4.2万人学习

swoole进程树解析
swoole进程树解析

共4课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号