本节讲解几个多进程的实例。

多进程实例

Master-Worker结构

下面例子实现了简单的多进程管理:

  • 支持设置最大子进程数
  • Master-Worker结构:Worker挂掉,Master进程会重新创建一个
<?php 

$pids = []; //存储子进程pid
$MAX_PROCESS = 3;//最大进程数 $pid = pcntl_fork();
if($pid <0){
exit("fork fail\n");
}elseif($pid > 0){
exit;//父进程退出
}else{
// 从当前终端分离
if (posix_setsid() == -1) {
die("could not detach from terminal");
} $id = getmypid();
echo time()." Master process, pid {$id}\n"; for($i=0; $i<$MAX_PROCESS;$i++){
start_worker_process();
} //Master进程等待子进程退出,必须是死循环
while(1){
foreach($pids as $pid){
if($pid){
$res = pcntl_waitpid($pid, $status, WNOHANG);
if ( $res == -1 || $res > 0 ){
echo time()." Worker process $pid exit, will start new... \n";
start_worker_process();
unset($pids[$pid]);
}
}
}
}
} /**
* 创建worker进程
*/
function start_worker_process(){
global $pids;
$pid = pcntl_fork();
if($pid <0){
exit("fork fail\n");
}elseif($pid > 0){
$pids[$pid] = $pid;
// exit; //此处不可退出,否则Master进程就退出了
}else{
//实际代码
$id = getmypid();
$rand = rand(1,3);
echo time()." Worker process, pid {$id}. run $rand s\n";
while(1){
sleep($rand);
}
}
}

~~~防盗版声明:本文系原创文章,发布于公众号飞鸿影的博客(fhyblog)及博客园,转载需作者同意。~~~

多进程Server

下面我们使用多进程实现一个tcp服务器,支持:

  • 多进程处理客户端连接
  • 子进程退出,Master进程会重新创建一个
  • 支持事件回调
<?php 

class TcpServer{
const MAX_PROCESS = 3;//最大进程数
private $pids = []; //存储子进程pid
private $socket; public function __construct(){
$pid = pcntl_fork();
if($pid <0){
exit("fork fail\n");
}elseif($pid > 0){
exit;//父进程退出
} else{
// 从当前终端分离
if (posix_setsid() == -1) {
die("could not detach from terminal");
} umask(0); $id = getmypid();
echo time()." Master process, pid {$id}\n"; //创建tcp server
$this->socket = stream_socket_server("tcp://0.0.0.0:9201", $errno, $errstr);
if(!$this->socket) exit("start server err: $errstr --- $errno");
}
} public function run(){
for($i=0; $i<self::MAX_PROCESS;$i++){
$this->start_worker_process();
} echo "waiting client...\n"; //Master进程等待子进程退出,必须是死循环
while(1){
foreach($this->pids as $k=>$pid){
if($pid){
$res = pcntl_waitpid($pid, $status, WNOHANG);
if ( $res == -1 || $res > 0 ){
echo time()." Worker process $pid exit, will start new... \n";
$this->start_worker_process();
unset($this->pids[$k]);
}
}
}
sleep(1);//让出1s时间给CPU
}
} /**
* 创建worker进程,接受客户端连接
*/
private function start_worker_process(){
$pid = pcntl_fork();
if($pid <0){
exit("fork fail\n");
}elseif($pid > 0){
$this->pids[] = $pid;
// exit; //此处不可退出,否则Master进程就退出了
}else{
$this->acceptClient();
}
} private function acceptClient()
{
//子进程一直等待客户端连接,不能退出
while(1){
$conn = stream_socket_accept($this->socket, -1);
if($this->onConnect) call_user_func($this->onConnect, $conn); //回调连接事件 //开始循环读取消息
$recv = ''; //实际收到消息
$buffer = ''; //缓冲消息
while(1){
$buffer = fread($conn, 20); //没有收到正常消息
if($buffer === false || $buffer === ''){
if($this->onClose) call_user_func($this->onClose, $conn); //回调断开连接事件
break;//结束读取消息,等待下一个客户端连接
} $pos = strpos($buffer, "\n"); //消息结束符
if($pos === false){
$recv .= $buffer;
}else{
$recv .= trim(substr($buffer, 0, $pos+1)); if($this->onMessage) call_user_func($this->onMessage, $conn, $recv); //回调收到消息事件 //客户端强制关闭连接
if($recv == "quit"){
echo "client close conn\n";
fclose($conn);
break;
} $recv = ''; //清空消息,准备下一次接收
}
}
}
} function __destruct() {
@fclose($this->socket);
}
} $server = new TcpServer(); $server->onConnect = function($conn){
echo "onConnect -- accepted " . stream_socket_get_name($conn,true) . "\n";
fwrite($conn,"conn success\n");
}; $server->onMessage = function($conn,$msg){
echo "onMessage --" . $msg . "\n";
fwrite($conn,"received ".$msg."\n");
}; $server->onClose = function($conn){
echo "onClose --" . stream_socket_get_name($conn,true) . "\n";
fwrite($conn,"onClose "."\n");
}; $server->run();

运行:

$ php process_multi.server.php
1528734803 Master process, pid 9110
waiting client...

此时服务端已经变成守护进程了。新开终端,我们使用ps命令查看进程:

$ ps -ef | grep php
yjc 9110 1 0 00:33 ? 00:00:00 php process_multi.server.php
yjc 9111 9110 0 00:33 ? 00:00:00 php process_multi.server.php
yjc 9112 9110 0 00:33 ? 00:00:00 php process_multi.server.php
yjc 9113 9110 0 00:33 ? 00:00:00 php process_multi.server.php
yjc 9134 8589 0 00:35 pts/1 00:00:00 grep php

可以看到4个进程:1个主进程,3个子进程。使用kill命令结束子进程,主进程会重新拉起一个新的子进程。

然后我们使用telnet测试连接:

$ telnet 127.0.0.1 9201
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
conn success
hello server!
received hello server!
quit
received quit
Connection closed by foreign host.

PHP多进程系列笔记(三)的更多相关文章

  1. PHP多进程系列笔记(五)

    前面几节都是讲解pcntl扩展实现的多进程程序.本节给大家介绍swoole扩展的swoole_process模块. swoole多进程 swoole_process 是swoole提供的进程管理模块, ...

  2. PHP多进程系列笔记(二)

    上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识. 僵尸(zombie)进程 这里说下僵尸进程: 僵尸进程是指的父进程已经退出,而该进程de ...

  3. PHP多进程系列笔记(一)

    本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识. PCNTL在PHP中进程控制支持默认是关闭的.您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或 ...

  4. PHP多进程系列笔记(四)

    本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识.本节难度较低. Posix常用函数 posix_kill 向指定pid进程发送信号.成功时返回 TRUE , 或者在失败时返回 ...

  5. 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API

    https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...

  6. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  7. Java系列笔记(4) - JVM监控与调优

    目录 参数设置收集器搭配启动内存分配监控工具和方法调优方法调优实例     光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分析原因并解决之.通过学习,我觉得JVM ...

  8. 跟着鸟哥学Linux系列笔记1

    跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 装完linux之后,接下来一步就是进行相关命令的学习了 第五章:首次登录与在线求助man page 1. X ...

  9. 《MFC游戏开发》笔记三 游戏贴图与透明特效的实现

    本系列文章由七十一雾央编写,转载请注明出处. 313239 作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5 ...

随机推荐

  1. 20145232 韩文浩 《Java程序设计》第9周学习总结

    教材学习内容总结 学习目标 了解JDBC架构 掌握JDBC架构 掌握反射与ClassLoader 了解自定义泛型和自定义枚举 会使用标准注解 JDBC标准主要分为两个部分:JDBC应用程序开发者接口和 ...

  2. c需要注意的细节

    1.在纯的.c文件中,例如struct Stu,之后不可以只使用Stu作为关键字来表示这个定义的结构体类型,一定要使用struct Stu一起作为类似int这种关键字来定义或者获取size. 2.函数 ...

  3. 浅尝辄止之MongoDB

    1 简介 MongoDB是一个介于关系数据库和非关系数据库之间的产品,基于分布式文件存储的数据库,旨在为WEB应用提供可扩展的高性能数据存储 解决方案.MongoDB将数据存储为一个文档,数据结构由键 ...

  4. web-day15

    第15章WEB15-AJAX和JQuery案例篇 今日任务 使用AJAX完成用户名的异步校验 使用JQuery完成用户名异步校验 使用JQuery完成商品信息模糊显示 使用JQuery完成省市联动效果 ...

  5. java基础-day20

    第09天 IO流 今日内容介绍 u  File类 u  字符流与字节流 第1章   File类 1.1      File概述 打开API,搜索File类.阅读其描述:File文件和目录路径名的抽象表 ...

  6. html5打开摄像头并用canvas模拟拍照 - 转

    <video id="video" width="640" height="480" autoplay></video&g ...

  7. Codeforces821A Okabe and Future Gadget Laboratory 2017-06-28 14:55 80人阅读 评论(0) 收藏

    A. Okabe and Future Gadget Laboratory time limit per test 2 seconds memory limit per test 256 megaby ...

  8. django创建分页

    前台html代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  9. cpu、内存、raid初识

    计算机基础 1u = 4.45cm dmidecode -s system-product-name 查看linux系统是物理机还是虚拟机 `逻辑CPU个数: cat /proc/cpuinfo | ...

  10. 解决toad中number类型小数位数过长按科学计数法显示的问题

    在toad中->view->option->data->display large number in scientific notation,不选择该选项即可. (在pl/s ...