使用 swoole_process 实现 PHP 进程池
swoole_process 主要是用来代替 PHP 的 pcntl 扩展。我们知道 pcntl 是用来进行多进程编程的,而 pcntl 只提供了 fork 这样原始的接口,容易使用错误,并且没有提供进程间通信以及重定向标准输入输出的功能。
而 swoole_process 则提供了比 pcntl 更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。
本文使用 swoole_process 与 EventLoop 完成一个 php 的进程池,并且支持动态创建新进程。
EventLoop
swoole 有一个 Reactor 线程,这个线程可以说是对 epoll 模型的封装,可以设置 read 事件和 write 事件的监听回调函数。
下面会用到一个函数:
bool swoole_event_add(mixed $sock, mixed $read_callback, mixed $write_callback = null, int $flags = null);
- 参数1为一个文件描述符,包括swoole_client->$sock、swoole_process->$pipe或者其他 fd(socket_create创建的资源 ,stream_socket_client/fsockopen创建的资源)
- 参数2为可读事件回调函数
- 参数3为可写事件回调函数
多进程编程少不了进程之间的通讯,swoole 的进程之间有两种通信方式,一种是消息队列(queue),另一种是管道(pipe)。那么本文使用的是 pipe 的方式。
下面是一个定时向进程池投递任务的例子。
代码:
<?php
class ProcessPool{
    private $process;
    /**
     * Worker 进程数组
     * @var array
     */
    private $process_list = [];
    /**
     * 正在被使用的进程
     * @var array
     */
    private $process_use = [];
    /**
     * 最少进程数量
     * @var int
     */
    private $min_worker_num = 3;
    /**
     * 最多进程数量
     * @var int
     */
    private $max_worker_num = 6;
    /**
     * 当前进程数量
     * @var int
     */
    private $current_num;
    public function __construct()
    {
        $this->process = new swoole_process(array($this, 'run'), false, 2);
        $this->process->start();
        swoole_process::wait();
    }
    public function run()
    {
        $this->current_num = $this->min_worker_num;
        //创建所有的worker进程
        for($i = 0; $i < $this->current_num; $i++){
            $process = new swoole_process(array($this, 'task_run'), false, 2);
            $pid = $process->start();
            $this->process_list[$pid] = $process;
            $this->process_use[$pid] = 0;
        }
        foreach($this->process_list as $process){
            swoole_event_add($process->pipe, function ($pipe) use ($process){
                $data = $process->read();
                var_dump($data . '空闲');
                //接收子进程处理完成的信息,并且重置为空闲
                $this->process_use[$data] = 0;
            });
        }
        //每秒定时向worker管道投递任务
        swoole_timer_tick(1000 ,function ($timer_id){
            static $index = 0;
            $index = $index + 1;
            $flag = true; //是否新建worker
            foreach ($this->process_use as $pid => $used){
                if($used == 0){
                    $flag = false;
                    //标记为正在使用
                    $this->process_use[$pid] = 1;
                    // 在父进程内调用write,子进程可以调用read接收此数据
                    $this->process_list[$pid]->write($index. "hello");
                    break;
                }
            }
            if($flag && $this->current_num < $this->max_worker_num){
                //没有闲置worker,新建worker来处理
                $process = new swoole_process(array($this, 'task_run'), false, 2);
                $pid = $process->start();
                $this->process_list[$pid] = $process;
                $this->process_use[$pid] = 1;
                $this->process_list[$pid]->write($index. "hello");
                $this->current_num++;
            }
            var_dump('第' .$index. '个任务');
            if($index == 10){
                foreach($this->process_list as $process){
                    $process->write("exit");
                }
                swoole_timer_clear($timer_id);
                $this->process->exit();
            }
        });
    }
    /**
     * 子进程处理
     * @param $worker
     */
    public function task_run($worker)
    {
        swoole_event_add($worker->pipe, function($pipe)use($worker){
            $data = $worker->read();
            var_dump($worker->pid . ':' . $data);
            if($data == 'exit'){
                $worker->exit();
                exit;
            }
            //模拟耗时任务
            sleep(5);
            //告诉主进程处理完成
            //在子进程内调用write,父进程可以调用read接收此数据
            $worker->write($worker->pid);
        });
    }
}
new ProcessPool();
首先定义几个重要的属性:
- $process_list :Worker 进程数组
- $process_use:正在被使用的进程
- $min_worker_num :最少进程数量
- $max_worker_num :最多进程数量
- $current_num :当前进程数量
- $process : 主进程
在实例化的时候创建主进程,并且运行 run 方法,在 run 方法里面先创建所有的 worker 进程,并且设置为空闲状态。
接着遍历所有的 worker 进程,并且加入 EventLoop 中,设置可读事件,用于接收子进程的空闲信号。
最后每隔一秒向 worker 进程投递任务。动态扩充进程池则在这里实现,如果没有闲置的进程,而此时又有新的任务,则需要动态创建一个新的进程并且置为繁忙状态。由于只模拟了十次任务,则第十个任务完成之后在父进程中发送 exit 使所有子进程退出。
运行效果与图解:

参考链接:
https://wiki.swoole.com/wiki/...
https://opso.coding.me/2018/0...
原文地址:https://segmentfault.com/a/1190000016824172
使用 swoole_process 实现 PHP 进程池的更多相关文章
- Swoole_process实现进程池的方法
		Swoole 的进程之间有两种通信方式,一种是消息队列(queue),另一种是管道(pipe),对swoole_process 的研究在swoole中显得尤为重要. 预备知识 IO多路复用 swool ... 
- python进程池:multiprocessing.pool
		本文转至http://www.cnblogs.com/kaituorensheng/p/4465768.html,在其基础上进行了一些小小改动. 在利用Python进行系统管理的时候,特别是同时操作多 ... 
- 64位进程池HashCode兼容处理
		背景 net旧项目使用32位生成的HashCode,存储到数据库中.迁移到64位上,就需要对HashCode做兼容处理. 解决方案 1:进程池配置支持32位程序. 2:对Hashcode做兼容处理,[ ... 
- Linux客户/服务器程序设计范式2——并发服务器(进程池)
		引言 让服务器在启动阶段调用fork创建一个子进程池,通过子进程来处理客户端请求.子进程与父进程之间使用socketpair进行通信(为了方便使用sendmsg与recvmsg,如果使用匿名管道,则无 ... 
- PYTHON多进程编码结束之进程池POOL
		结束昨晚开始的测试. 最后一个POOL. A,使用POOL的返回结果 #coding: utf-8 import multiprocessing import time def func(msg): ... 
- python(进程池/线程池)
		进程池 import multiprocessing import time def do_calculation(data): print(multiprocessing.current_proce ... 
- python进程池剖析(三)
		之前文章对python中进程池的原理.数据流以及应用从代码角度做了简单的剖析,现在让我们回头看看标准库中对进程池的实现都有哪些值得我们学习的地方.我们知道,进程池内部由多个线程互相协作,向客户端提供可 ... 
- python进程池剖析(二)
		之前文章中介绍了python中multiprocessing模块中自带的进程池Pool,并对进程池中的数据结构和各个线程之间的合作关系进行了简单分析,这节来看下客户端如何对向进程池分配任务,并获取结果 ... 
- python进程池剖析(一)
		python中两个常用来处理进程的模块分别是subprocess和multiprocessing,其中subprocess通常用于执行外部程序,比如一些第三方应用程序,而不是Python程序.如果需要 ... 
随机推荐
- STM8S103汇编文档和注意
			1.官方文档<STM8 CPU programming manual>介绍了指令和寻址方式 2.辅助类文档,boot loader文档<STM8 bootloader> 3.关 ... 
- 3ds Max制作客厅场景实例教程
			附件系列 (图01) 让我们回顾一下场景:一个房间包括下列一件件家具, 在中间的一张小桌子,在房间的角落的一个小桌子,有一个垃圾桶和一个带镜子的边桌,有一个烛台.还有一个挂钟,窗帘,沙发和带手臂的椅子 ... 
- B. Recursive Queries 打表
			Code: #include<cstdio> #include<iostream> #include<algorithm> #include<cstring& ... 
- POJ3764 The xor-longest Path(字典树)
			题意 给你一棵树,n个节点,n-1条边每条边i都有一个权值wi.定义任意两点间的权值为:这两点间的路径上的所有边的值的异或.比如a点和b点间有i,j,k三条边,那么ab两点间的权值为:wi^wj^wk ... 
- [USACO4.1]篱笆回路Fence Loops
			题目:USACO Training 4.1(在官网上提交需加文件输入输出).洛谷P2738. 题目大意:给你一张图里的边集,让你求出这张图的最小环. 解题思路:求最小环很简单,用Floyd即可.最重要 ... 
- fdisk 添加逻辑分区
			[root@riyimei ~]# fdisk /dev/sdb WARNING: DOS-compatible mode is deprecated. It's strongly recommend ... 
- 每个人都能实现的vue自定义指令
			前文 先来bb一堆废话哈哈.. 用vue做项目也有一年多了.除了用别人的插件之外.自己也没尝试去封装指令插件之类的东西来用. 刚好最近在项目中遇到一个问题.(快速点击按钮多次触发多次绑定的方法),于是 ... 
- win系统安装node出现这个2503和2502解决办法
			一: 今天在公司的新电脑要安装appium,所以要搭建appium的环境,所以在安装到node的时候,出现了内部错误2503和2502,安装中断. 这种错误可能是权限不足导致,一般“.exe”程序可以 ... 
- 3D拾取技术
			在unity3d中用户通过触摸屏选中虚拟3D世界中的物体进行操控,就须要掌握3d 拾取技术. 3d拾取技术很的简单:由摄像机与屏幕上的触控点之间确定一条射线.由此射线射向3d世界, 最先和此射线相交的 ... 
- 新手做2D手游该用哪些工具?
			全球手游行业规模将突破250亿美元,越来越多的开发者开始进入手游研发领域,而作为一名菜鸟,很多时候,如果没有其他开发者的建议,会走很多弯路.一开始进入游戏研发领域的时候,你很难知道该选择什么工具.什么 ... 
