PHP并发IO编程并发IO一直是服务端编程的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,再到现在的异步IO,协作程序。因为PHP程序员拥有强大的LAMP框架,所以对这类底层知识知之甚少。本文旨在详细介绍PHP对并发IO编程的各种尝试,最后介绍Swoole的使用,深入浅出全面分析并发IO问题。多进程/多线程同步阻塞最早的服务器端程序是通过多进程多线程来解决并发IO的问题。进程模型最早出现,进程的概念从Unix系统诞生时就已经有了。最早的服务器端程序一般是接受一个客户端连接创建一个进程,然后子进程进入一个循环,以同步阻塞的方式与客户端连接进行交互,发送和接收数据。后来出现了多线程模式。与进程相比,线程更轻,线程之间共享内存栈,因此不同线程之间的交互很容易实现。例如,在聊天室这样的程序中,客户端连接可以相互交互,聊天室??中的玩家可以向任何其他人发送消息。多线程方式实现起来非常简单,线程可以直接向某个客户端连接发送数据。多进程模式需要使用管道、消息队列和共享内存,这些统称为进程间通信(IPC)和复杂技术。代码示例:多进程/线程模型的过程是创建socket,绑定服务器端口(bind),监听端口(listen)。以上三个步骤可以使用PHP中的stream_socket_server函数来完成。当然也可以使用下层的sockets扩展单独实现。进入while循环,阻塞在accept操作上,等待客户端连接进入。此时,程序会进入休眠状态,直到有新的客户端向服务器发起连接,操作系统才会唤醒该进程。accept函数返回客户端连接的socket主进程。多进程模型下使用fork(php:pcntl_fork)创建子进程,多线程模型下使用pthread_create(php:newThread)创建子线程。如果下面没有特殊说明,会同时使用process来表示process/thread。子进程创建成功后,进入while循环,阻塞在recv(php:fread)调用上,等待客户端向服务端发送数据。服务端程序收到数据后进行处理,然后使用send(php:fwrite)向客户端发送响应。长连接服务会继续与客户端交互,而短连接服务一般会在收到响应后关闭。当客户端连接关闭时,子进程退出并销毁所有资源。主进程会回收子进程。这种模型最大的问题是进程/线程的创建和销毁非常昂贵。所以上面的模式不能应用于非常繁忙的服务器程序。相应的改进版本解决了这个问题,就是经典的Leader-Follower模型。代码示例:其特点是程序启动后会创建N个进程。每个子进程进入Accept,等待新的连接进来。当客户端连接到服务器时,其中一个子进程被唤醒,开始处理客户端请求,不再接受新的TCP连接。当这个连接关闭时,子进程会被释放,重新进入Accept,参与处理新的连接。这种模型的好处是流程可以完全复用,不需要额外的消耗,性能非常好。很多常见的服务器程序都是基于这种模型的,比如Apache和PHP-FPM。多进程模型也有一些缺点:这种模型严重依赖进程数来解决并发问题。一个客户端连接需要占用一个进程,工作进程的数量取决于并发处理能力。操作系统可以创建的进程数是有限的。启动大量进程会带来额外的进程调度消耗。当有数百个进程时,进程上下文切换调度的消耗可能占CPU的不到1%,可以忽略不计。如果启动几千甚至几万个进程,消耗就会暴增。调度消耗可能占CPU的百分之几十甚至百分之一百。另外还有一些场景是多进程模型解决不了的,比如即时通讯程序(IM),一个服务器要同时维护几万甚至几十万上百万的连接(经典的C10K问题),多进程模型不能如愿以偿。另一种场景也是多进程模型的弱点。通常Web服务器启动100个进程。如果一个请求消耗100ms,100个进程可以提供1000qps,这是一个不错的处理能力。但是如果请求需要调用外网Http接口,比如QQ、微博登录等,时间会比较长,一个请求需要10s。那一个进程每秒只能处理0.1个请求,100个进程也只能达到10qps,处理能力太差了。有没有一种技术可以在一个进程中处理所有并发IO?答案是肯定的,这就是IO多路复用技术。IO多路复用/事件循环/异步非阻塞其实IO多路复用的历史和多进程一样悠久。Linux很早就提供了select系统调用,一个进程可以维持1024个连接。后来增加了poll系统调用,poll做了一些改进,解决了1024个限制的问题,可以保持任意数量的连接。但是select/poll还有一个问题就是需要循环检测连接上是否有事件。这就是问题所在。如果服务器有100万个连接,某一时刻只有一个连接向服务器发送数据,则select/poll需要循环100万次,其中只有1次命中,其余99万次9999次无效,白白浪费CPU资源。直到Linux2.6内核提供了一个新的epoll系统调用,无需轮询即可保持无限个连接,这才真正解决了C10K的问题。现在各种高并发的异步IO服务器程序都是基于epoll实现的,比如Nginx、Node.js、Erlang、Golang。像Node.js这样的单进程单线程程序,可以维持100万以上的TCP连接,这都要归功于epoll技术。IO多路复用异步非阻塞程序采用经典的Reactor模型。顾名思义,Reactor是反应器的意思,它不处理任何数据的发送和接收。只能监听一个套接字句柄的事件变化。Reactor有4个核心操作:addAddsockettomonitortoreactor,可以是listensocket或者clientsocket,也可以设置修改事件监听,比如pipeline,eventfd,signal等。可以设置监听的类型,比如readable,可以写。它易于阅读且易于理解。对于listensocket,表示有新的客户端连接到达,需要接受。对于客户端连接是接收数据,recv是必需的。可写事件有点难以理解。SOCKET有一个缓冲区。如果要给客户端连接发送2M的数据,是不能一次性发送的。操作系统默认的TCP缓冲区只有256K。一次只能发送256K,缓冲区满后send会返回EAGAIN错误。这时候就需要监听可写事件了。在纯异步编程中,需要监听可写事件,保证发送操作是完全非阻塞的。del从反应器中移除,不再监听事件。回调是事件发生后相应的处理逻辑。它通常在添加/设置期间制定。C语言是用函数指针实现的,JS可以使用匿名函数,PHP可以使用匿名函数、对象方法数组、字符串函数名。Reactor只是一个事件生成器,对socket句柄的实际操作,如connect/accept、send/recv、close,都是在回调中完成的。具体编码可以参考如下伪代码:Reactor模型还可以结合多进程、多线程使用,既实现了异步非阻塞IO,又利用了多核。目前流行的异步服务器程序都是这样的:比如Nginx:多进程ReactorNginx+Lua:多进程Reactor+协程Golang:单线程Reactor+多线程协程Swoole:多线程Reactor+多进程Worker协程协程是什么,从底层技术来看,其实是一种异步IOReactor模型。应用层自己实现任务调度,利用Reactor在当前执行的用户态线程之间切换,但是Reactor的存在对用户代码是完全感知不到的。PHP并发IO编程实践PHP相关扩展Stream:PHP内核提供的socket包Sockets:底层SocketAPI的包Libevent:libevent库的包Event:基于Libevent更高级的包,提供面向对象的接口,定时器,和信号处理支持Pcntl/Posix:多进程、信号、进程管理支持Pthread:多线程、线程管理、锁支持PHP和相关扩展,涉及共享内存、信号量、消息队列PECL:PHP扩展库,包括系统的底层,数据分析、算法、驱动、科学计算、图形等一应俱全。如果在PHP标准库中没有找到,可以在PECL中寻找需要的函数。PHP语言的优缺点PHP的优点:第一个就是简单,PHP比其他任何语言都简单,如果上手的话,一个星期就可以真正上手PHP。有一本关于C++的书,叫做《21天深入学习C++》。其实21天是不可能学会的。甚至可以说,C++没有3-5年是无法深入掌握的。但是PHP绝对可以7天上手。所以PHP程序员的数量非常多,招聘也比其他语言容易。PHP非常强大,因为PHP官方的标准库和扩展库提供了99%可以用于服务器端编程的东西。PHP的PECL扩展库中您想要的任何函数。另外,PHP已经有20多年的历史,生态系统非常庞大。你可以在Github上找到很多代码。PHP的缺点:性能比较差,因为毕竟是动态脚本,不适合密集计算。同样的PHP程序如果用C/C++写,PHP版本会比它差一百倍。大家都知道函数命名标准很差。PHP更注重实用性,没有一些标准。有些函数的命名很混乱,每次都得翻PHP手册。提供的数据结构和函数的接口粒度比较粗。PHP只有一种Array数据结构,底层是基于HashTable的。PHP的Array集成了Map、Set、Vector、Queue、Stack、Heap等数据结构的功能。另外,PHP有一个SPL,提供对其他数据结构的类封装。因此,PHPPHP更适合实际应用层面的程序。PHP作为业务开发和快速实施的利器,并不适合开发底层软件。C/C++、JAVA、Golang等静态编译语言作为PHP的补充。动静结合使用IDE工具实现自动补全。,语法提示更多学习内容可以访问【与各大厂商对比】优质PHP架构师教程目录。只要能读懂,就能保证你的薪水更上一层楼(持续更新中)。以上内容希望对大家有所帮助。很多PHPer再高级,总会遇到一些问题和瓶颈。业务代码写多了没有方向感。我不知道从哪里开始改进。我整理了一些这方面的资料,包括但不限于:性能、高并发、服务器性能调优、TP6、laravel、YII2、Redis、Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等知识点进阶进阶干货免费分享给大家,有需要的可以点此进阶PHP月薪30k>>>架构师成长之路【免费获取视频和面试资料】
