php-fpm与swoole
php-fpm与swoole
php本身是单进程单线程的,那么它是怎么解决并发问题的呢?这就涉及到本文将要提及的php-fpm和swoole
一、php-fpm(FastCGI 进程管理器)
1. 生命周期
php-fpm的生命周期如图:
2. 工作原理
php-fpm启动->生成n个fast-cgi协议处理进程->监听一个端口等待任务用户请求->web服务器接收请求->请求转发给php-fpm->php-fpm交给一个空闲进程处理->进程处理完成->php-fpm返回给web服务器->web服务器接收数据->返回给用户
nginx + php-fpm 就是用的以上的方法。
3. 优缺点
优点:nginx+php-fpm场景下php的单线程并不妨碍处理并发。当并发来了fpm会启动更多的worker去处理。并没有发生阻塞。
缺点:
1)这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的。
2)启动大量进程会带来额外的进程调度消耗。数百个进程时可能进程上下文切换调度消耗占CPU不到1%可以忽略不计,如果启动数千甚至数万个进程,消耗就会直线上升。调度消耗可能占到 CPU 的百分之几十甚至 100%。
3)有一些场景多进程模型无法解决
比如即时聊天程序(IM),一台服务器要同时维持上万甚至几十万上百万的连接(经典的C10K问题),多进程模型就力不从心了。
还有一种场景也是多进程模型的软肋。通常Web服务器启动100个进程,如果一个请求消耗100ms,100个进程可以提供1000qps,这样的处理能力还是不错的。但是如果请求内要调用外网Http接口,像QQ、微博登录,耗时会很长,一个请求需要10s。那一个进程1秒只能处理0.1个请求,100个进程只能达到10qps,这样的处理能力就太差了。
4. 有没有一种技术可以在一个进程内处理所有并发IO呢?
三、Swoole
swoole的进程管理模式与php-fpm并没有本质上的区别。同样还是一个master多个worker进程。但是不同的是一个同步阻塞。一个异步非阻塞。
1、swoole的生命周期
如下图:
分析:
php-fpm在执行代码的时候遇到IO(数据库读写,缓存读写,文件读写)会同步阻塞。后面的任务必须等前面的任务执行完了之后才会接着执行。这种模式的性能是不如swoole的异步执行的。同时因为性能不足还引发了一个更重要的问题。当并发来的时候,进程数达到了最大限制,fpm就会进入等待。当一个进程释放之后才能继续执行代码。
而swoole因为单个进程执行速度更快,就能更快的释放掉,也就比fpm更不容易等待进程。这样就能比fpm承载更大的QPS。
2、Swoole的进程模型
多进程的实现一般会被设计Master-Worker模式,常见的nginx默认的多进程模式也正是如此,当然swoole默认的也是多进程模型
相比Master-Worker模式,swoole的进程模型可以用Master-Manager-Worker来形容。即在Master-Worker的基础上又增加了一层Manager进程。
正所谓“存在即合理”,我们来看一下Master\Manager\Worker三种进程各自存在的原因。
Master进程是一个多线程程序。注解:按照我们之前的理解,多个线程是运行在单一进程的上下文中的,其实对于单一进程中的每一个线程,都有它自己的上下文,但是由于共同存在于同一进程,所以它们也共享这个进程,包括它的代码、数据等等。
再回来继续说Master进程,Master进程就是我们的主进程,掌管生杀大权,它挂了,那底下的都得玩完。Master进程,包括主线程,多个Reactor线程等。
每一个线程都有自己的用途,比如主线程用于Accept、信号处理等操作,而Reactor线程是处理tcp连接,处理网络IO,收发数据的线程。
说明两点:
1)主线程的Accept操作,socket服务端经常用accept阻塞
2)信号处理
信号就相当于一条消息,比如我们经常操作的 Ctrl+C 其实就是给Master进程的主线程发送一个SIGINT的信号,意思就是你可以终止啦,其实除此之外,信号有很多种。
通常,主线程处理完新的连接后,会将这个连接分配给固定的Reactor线程,并且这个Reactor线程会一直负责监听此socket(socket即套接字,是用来与另一个进程进行跨网络通信的文件,文件可读可写),换句话就是说当此socket可读时,会读取数据,并将该请求分配给worker进程;当此socket可写时,会把数据发送给tcp客户端。
用一张图清晰的梳理下
3、Manager进程的作用
1)疑问
疑问:那swoole为什么不能像Nginx一样,是Master-Worker进程结构的呢?Manager进程的作用是什么呢?
我们知道,在Master-Worker模型中,Master只有一个,Worker是由父进程Master进程复制出来的,且Worker进程可以有多个。
注解:在linux中,父进程可以通过调用fork函数创建一个新的子进程,子进程是父进程的一个副本,几乎但不完全相同,二者的最大区别就是都拥有自己独立的进程ID,即PID。
对于多线程的Master进程而言,想要多Worker进程就必须fork操作,但是fork操作是不安全的,所以,在swoole中,有一个专职的Manager进程,Manager进程就专门负责worker/task进程的fork操作和管理。换句话也就是说,对于worker进程的创建、回收等操作全权有“保姆”Manager进程进行管理。
2)fork操作
关于fork操作不安全,这里扩展一下:
在Linux中,fork的时候只复制当前线程到子进程,在fork(2)-Linux Man Page中有着这样一段相关的描述:
The child process is created with a single thread--the one that called fork(). The entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects; the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause.
也就是说除了调用fork的线程外,其他线程在子进程中“蒸发”了。
这就是多线程中fork所带来的一切问题的根源所在了。
通常,worker进程被误杀或者由于程序的原因会异常退出,Manager进程为了保证服务的稳定性,会重新拉起新的worker进程,意思就是Worker进程你发生意外“死”了,没关系,我自身不“死”,就可以fork千千万万个你。
当然,Master进程和Manager进程我们是不怎么关心的,真正实现业务逻辑,是在worker/task进程内完成的。
再来一张图梳理下Manager进程和Worker/Task进程的关系:
四、协程是什么
协程是一种轻量级的线程,由用户代码来调度和管理,而不是由操作系统内核来进行调度,也就是在用户态进行。可以直接的理解为就是一个非标准的线程实现,但什么时候切换由用户自己来实现,而不是由操作系统分配 CPU 时间决定。
具体来说,Swoole 的每个Worker进程会存在一个协程调度器来调度协程,协程切换的时机就是遇到 I/O 操作或代码显性切换时,进程内以单线程的形式运行协程,也就意味着一个进程内同一时间只会有一个协程在运行且切换时机明确,也就无需处理像多线程编程下的各种同步锁的问题。
协程从底层技术角度看实际上还是异步 IO Reactor模型,应用层自行实现了任务调度,借助 Reactor 切换各个当前执行的用户态线程,但用户代码中完全感知不到Reactor的存在。
作用:节省出进程中因为IO操作而阻塞等待的时间
参考链接:
http://rango.swoole.com/
http://www.manks.top/swoole-process-model.html
https://www.jianshu.com/p/64e52f6f4837
https://www.easyswoole.com/
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析