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

多进程实例

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. 后台开发 - DPDK引发的图谱

    关系图谱(点击看完整大图): 部分名词: 名词 全写 解释 备注 DPDK Data Plane Development Kit 数据平面开发套件或叫数据平面开发工具集 Intel开源的快速数据包处理 ...

  2. (转)ASP.NET MVC 3和Razor中的@helper 语法

    转自:http://kb.cnblogs.com/page/102191/ ASP.NET MVC 3支持一项名为“Razor”的新视图引擎选项(除了继续支持/加强现有的.aspx视图引擎外).当编写 ...

  3. 《计算机科学基础》学习笔记_Part 1 Computer and Data

    Technorati Tags: 计算机科学基础,读书笔记 Chapter 1. Introduction Ø  计算机:黑盒,Output Data=f(Input Data, Program) Ø ...

  4. TBB的学习

    1. TBB简介 TBB ( Thread Building Blocks, 线程构建模块) 是Intel公司开发的并行编程开发的工具.它支持Windows,OS X, Linux平台,支持的编译器有 ...

  5. 《mysql必知必会》学习_第三章_20180724_欢

    P16: use crashcourse; #选择数据库#使用crashcouse这个数据库,因为我没有crashcourse这个数据库,所以用我的hh数据库代替. P17: show databas ...

  6. 认识Hadoop

    概述 开源.分布式存储.分布式计算 大数据生态体系 特点:开源.社区活跃 囊括了大数据处理的方方面面 成熟的生态圈 推荐系统 应用场景 搭建大型数据仓库,PB级数据的存储.处理.分析.统计 日志分析 ...

  7. 编写高质量iOS与OS X代码的52个有效方法

    第一章重点: 第一条:OC的起源 OC由smalltalk语言演化而来的语言为消息结构(messaging structure)语言,其运行时所因执行的的代码由运行环境来决定:函数调用(functio ...

  8. asp.net core sdk & runtime 镜像[已更新至2.2.0]

    在官方镜像的脚本上, 增加了System.Drawing相关的依赖库 以北京时间为默认的时间 2.2.0 Windows SDK地址: 官方: https://dotnetcli.blob.core. ...

  9. WPF实现窗体中的悬浮按钮

    WPF实现窗体中的悬浮按钮,按钮可拖动,吸附停靠在窗体边缘. 控件XAML代码: <Button x:Class="SunCreate.Common.Controls.FloatBut ...

  10. 前端基础-html 字体标签,排版标签,超链接,图片标签

    主要内容: 字体标签: h1~h6.<font>.<u>.<b>.<strong><em>.<sup>.<sub> ...