简介

swoole_process 是swoole提供的进程管理模块,用来替代PHP的pcntl扩展。

首先,确保安装的swoole版本大于1.7.2:

$ php --ri swoole

swoole

swoole support => enabled
Version => 1.10.1

注意:swoole_process在最新的1.8.0版本已经禁止在Web环境中使用了,所以也只能支持命令行。这时候如果要做并发,multi-curl是不错的选择。https://group.swoole.com/question/106198

实例说明

本例里待消费的是三个shell命令,会分别创建一个子进程来消费。消费的时候故意sleep了1秒,以便直观看到效果。

process_t1.php

<?php

$start_time = microtime(TRUE);

$cmds = [
"uname",
"date",
"whoami"
]; foreach ($cmds as $cmd) {
$process = new swoole_process( "my_process", true); $process->start();
$process->write($cmd); //通过管道发数据到子进程。管道是单向的:发出的数据必须由另一端读取。不能读取自己发出去的 echo $rec = $process->read();//同步阻塞读取管道数据
} //子进程创建成功后要执行的函数
function my_process(swoole_process $worker){
sleep(1);//暂停1s $cmd = $worker->read(); // $return = exec($cmd);//exec只会输出命令执行结果的最后一行内容,且需要显式打印输出 ob_start();
passthru($cmd);//执行外部程序并且显示未经处理的、原始输出,会直接打印输出。
$return = ob_get_clean();
if(!$return) $return = 'null'; $worker->write($return);//写入数据到管道
} //子进程结束必须要执行wait进行回收,否则子进程会变成僵尸进程
while($ret = swoole_process::wait()){// $ret 是个数组 code是进程退出状态码,
$pid = $ret['pid'];
echo PHP_EOL."Worker Exit, PID=" . $pid . PHP_EOL;
} $end_time = microtime(TRUE);
echo sprintf("use time:%.3f s\n", $end_time - $start_time);

命令行里运行:

$ php process_t1.php  

Linux
Sat Apr 21 15:29:55 CST 2018
root Worker Exit, PID=672 Worker Exit, PID=674 Worker Exit, PID=676
use time:3.080 s

大家会觉得很奇怪,为什么开了三个子进程,还是用了3秒,应该是1秒左右才对呀。

原因是父进程读取子进程返回的数据的时候,是同步阻塞读取:

 echo $rec = $process->read();//同步阻塞读取管道数据

导致的后果就是父进程依次等待每个进程处理完并返回了内容,才走下一次循环。

解决方案1:

使用swoole_event_add将管道加入到事件循环中,变为异步模式:

// echo $rec = $process->read();//同步阻塞读取管道数据

//使用swoole_event_add将管道加入到事件循环中,变为异步模式
swoole_event_add($process->pipe, function($pipe) use($process) {
echo $rec = $process->read(); swoole_event_del($process->pipe);//socket处理完成后,从epoll事件中移除管道
});

执行结果:

Worker Exit, PID=686

Worker Exit, PID=687

Worker Exit, PID=688
use time:1.060 s
Linux
Sat Apr 21 15:37:14 CST 2018
root

大家会发现,use time数据并不是最后打印出来的。已经是异步的了。 实际执行时间1s左右。

解决方案2:

先不获取子进程返回值,循环结束后统一返回:

foreach ($cmds as $cmd) {
$process = new swoole_process( "my_process", true); $process->start();
$process->write($cmd); //通过管道发数据到子进程 $process_arr[] = $process;
} foreach ($process_arr as $process){
echo $rec = $process->read();
}

执行结果:

Linux
Sat Apr 21 15:52:24 CST 2018
root Worker Exit, PID=694 Worker Exit, PID=693 Worker Exit, PID=695
use time:1.061 s

函数原型

swoole_process::__construct(callable $function, $redirect_stdin_stdout = false, $create_pipe = true);

第一个参数是子进程创建成功后要执行的函数。

$redirect_stdin_stdout,重定向子进程的标准输入和输出。启用此选项后,在子进程内输出内容将不是打印屏幕,而是写入到主进程管道(例如用echo打印的内容也写入管道)。读取键盘输入将变为从管道中读取数据。默认为阻塞读取。

$create_pipe,是否创建管道,启用$redirect_stdin_stdout后,此选项将忽略用户参数,强制为true。如果子进程内没有进程间通信,可以设置为 false

进程常驻后台

如果跑的服务需要一直常驻后台,可以在$process->start();前面加上:

swoole_process::daemon();

服务会在后台运行。

更多示例

多进程获取网页状态码

<?php

//获取多个网页信息

$urls = [
'https://www.baidu.com',
'http://www.52fhy.com',
'http://www.52fhy.com/1',
'https://www.52fhy.com',
]; foreach ($urls as $key => $url) {
$process = new swoole_process(function(swoole_process $worker) use ($url){
$code = getHttpCode($url);
$worker->write($code);
}, true);
$process->start(); swoole_event_add($process->pipe, function($pipe) use($process, $url) {
echo sprintf("%s code: %s\n", $url, $process->read());
swoole_event_del($pipe);
});
} echo "ok.\n"; while($ret = swoole_process::wait()){
// echo PHP_EOL."Worker Exit, PID=" . $ret['pid'] . PHP_EOL;
} /**
* 获取网页http code
*/
function getHttpCode($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
// curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "HEAD");
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
curl_setopt ($ch, CURLOPT_TIMEOUT_MS, 1000);//超时时间
curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch); return (string)$info['http_code'];
}

运行:

$ php process_get.php
ok.
http://www.52fhy.com code: 403
http://www.52fhy.com/1 code: 404
https://www.baidu.com code: 200
https://www.52fhy.com code: 403

使用消息队列通信

<?php

//获取多个网页信息

$urls = [
'https://www.baidu.com',
'http://www.52fhy.com',
'http://www.52fhy.com/1',
'https://www.52fhy.com',
]; $process = new swoole_process(function(swoole_process $worker) use($urls) {
foreach ($urls as $url) {
$code = getHttpCode($url);
$worker->push($url.': '.$code);
}
$worker->push('exit');
}, false, false); //不创建管道
$process->useQueue(1, 2); //使用消息队列。消息队列通信方式与管道不可共用。消息队列不支持EventLoop,使用消息队列后只能使用同步阻塞模式非阻塞
$process->start(); while(1){
$ret = $process->pop();
if($ret == 'exit') break;
echo sprintf("%s\n", $ret);
} echo "ok.\n"; while($ret = swoole_process::wait()){
echo PHP_EOL."Worker Exit, PID=" . $ret['pid'] . PHP_EOL;
} /**
* 获取网页http code
*/
function getHttpCode($url){
//省略
}

运行:

$ php process_get_queue.php
https://www.baidu.com: 200
http://www.52fhy.com: 403
http://www.52fhy.com/1: 404
https://www.52fhy.com: 403
ok. Worker Exit, PID=1222

参考

1、Process

https://wiki.swoole.com/wiki/page/p-process.html

2、swoole_process->read

https://wiki.swoole.com/wiki/page/217.html

swoole_proces实现多进程的更多相关文章

  1. Python中的多进程与多线程(一)

    一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...

  2. 取代SharedPreferences的多进程解决方案

    Android的SharedPreferences用来存储一些键值对, 但是却不支持跨进程使用. 跨进程来用的话, 当然是放在数据库更可靠啦, 本文主要是给作者的新库PreferencesProvid ...

  3. python 多进程使用总结

    python中的多进程主要使用到 multiprocessing 这个库.这个库在使用 multiprocessing.Manager().Queue时会出问题,建议大家升级到高版本python,如2 ...

  4. Nginx深入详解之多进程网络模型

    一.进程模型        Nginx之所以为广大码农喜爱,除了其高性能外,还有其优雅的系统架构.与Memcached的经典多线程模型相比,Nginx是经典的多进程模型.Nginx启动后以daemon ...

  5. Python的多线程(threading)与多进程(multiprocessing )

    进程:程序的一次执行(程序载入内存,系统分配资源运行).每个进程有自己的内存空间,数据栈等,进程之间可以进行通讯,但是不能共享信息. 线程:所有的线程运行在同一个进程中,共享相同的运行环境.每个独立的 ...

  6. 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)

    Python的socket高级应用(多进程,协程与异步)

  7. PHP的pcntl多进程

    PHP使用PCNTL系列的函数也能做到多进程处理一个事务.比如我需要从数据库中获取80w条的数据,再做一系列后续的处理,这个时候,用单进程?你可以等到明年今天了...所以应该使用pcntl函数了. 假 ...

  8. 初探PHP多进程

    h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...

  9. gdb进程调试,多进程调试

    1.单进程的调试 常规的通过gdb cmd这种方式开启调试,特别说明的是通过attach的方法附加到一个指定的进程上去进行调试,这种方法适合于调试一个已经运行的进程,具体用法:  gdb -p [pi ...

随机推荐

  1. 从零部署Spring boot项目到云服务器(准备工作)

    自己的博客终于成功部署上线了,回过头来总结记录一下整个项目的部署过程! 测试地址:47.94.154.205:8084 注:文末有福利! 一.Linux下应用Shell通过SSH连接云服务器 //ss ...

  2. JavaScript(第二十四天)【事件对象】

    JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能.最方便和强大的就是事件对象,他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的 ...

  3. 记录python接口自动化测试--unittest框架基本应用(第二目)

    在第一目里写了几个简单demo,并把调用get和post请求的方法封装到了一个类里,这次结合python自带的unittest框架,用之前封装的方法来写一个接口测试demo 1.unittest简单用 ...

  4. Beta冲刺NO.7

    Beta冲刺 第七天 昨天的困难 昨天的困难在一些多表查询上,不熟悉hibernate的套路,走了很多弯路. 第一次使用图表插件,在图表的显示问题上花了一定的时间. 对于页面绑定和后台数据自动填充的理 ...

  5. 201621123060《JAVA程序设计》第十二周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 面向系统综合设计-图书馆管理系统或购物车 使用流与文件改造你的图书馆管理系统或购物车. 2.1 简述如何 ...

  6. 201621123040《Java程序设计》第3周学习总结

    1.本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词,如类.对象.封装等 面向对象的思想 对象 类 1.2 用思维导图或者Onenote或其他工具将这些关键词组织起来. 掌握的还不够深 ...

  7. RxSwift:ReactiveX for Swift 翻译

    RxSwift:ReactiveX for Swift 翻译 字数1787 阅读269 评论3 喜欢3 图片发自简书App RxSwift | |-LICENSE.md |-README.md |-R ...

  8. ubuntu1604使用源码方式安装ruby2.5.0

    本文介绍ubutntu1604环境下源代码方式安装ruby 版本2.5.0 如果内存小于2G可以开启虚拟内存,下面的命令开启4G虚拟内存 sudo dd if=/dev/zero of=/swap b ...

  9. kali使用

    1.kali安装后安装vmtools ①.vim /etc/apt/sources.list 添加中科大滚动版更新源 deb http://mirrors.ustc.edu.cn/kali kali- ...

  10. 使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API

    Hypermedia As The Engine Of Application State (HATEOAS) HATEOAS(Hypermedia as the engine of applicat ...