时间:2022-11-03 07:00:29 | 浏览:2856
PHP进程探究
PHP作为解释器运行通过线程或者进程都能实现(如果使用Apache,那么就可能使用多线程模型。使用php-fpm,就是使用多进程模型,这里以多进程模型解释)。服务器每接收到一个请求就要起一个PHP进程,平均一个PHP进程消耗内存2M左右(默认最大为8M,参数可以设置)。独立的进程让PHP能专一的做自己的解释工作,程序员也从复杂的代码逻辑中走出来,不用担心资源的竞争和各种锁问题。独立进程虽好但这也导致想通过多进程或者异步来提速成本非常的高(主要是开发难度)。如果一定要通过PHP实现多进程和异步其实是很容易做到的。
PHP有很多第三方扩展,比如Swoole能让PHP像Node一样实现异步。PHP官方扩展库pcntl_*能很简单的实现多进程。扩展虽好,但实际应用时切忌要慎重,便利的同时风险也来了。比如对多进程的控制,处理不好很容易导致程序死锁,CPU内存爆表、服务器宕机。异步回调的Coding方式与PHP本身的编程思想有一定出入,驾驭不好也是灾难。
当然也不能说的太吓人,在实际的项目中我们有很多场景不得不考虑通过多进程或者异步来优化程序。这里举一个很常见的例子『发送消息通知』,比如短信和邮件。这里说一个实际的场景:企业需要给200W用户发短信通知,短信接口支持最大100次/秒的调用频率,短信接口每次调用耗时300毫秒。如果单进程跑脚本的话,需要7天才能把短信发完。如果我们起30个进程,每秒能发送100条短信,6个小时内能发完,能提速30倍。优化方案确定之后,我们再看如何通过PHP去实现这样一个脚本。
一. pcntl扩展初探;
通过pcntl扩展创建多进程,参见如下代码;
function demo(array $phoneList){$cnt = count($phoneList); //测试数组大小
$slice = 30; //需要调用的进程数量
$master = array_chunk($phoneList,floor($cnt/$slice));
$childList = []; while($slice >= 0)
{
$pid = pcntl_fork(); if($pid > 0){
$childList[$pid] = 1; //$pid>0表示当前还在执行父进程的代码
//这里最好啥都不做,每次执行pcntl_fork都会执行这里的代码。
//这里的代码执行完之后 会将$pid设置为0,然后jump到pcntl_fork代码之后,重新做判断;
}elseif($pid == 0){ //这里写我们的逻辑
foreach($master[$slice] as $val)
{ //这里发生短信
echo sprintf("%s Child:%s ",$slice,$val);
} //子进程执行完之后务必需要关闭;
exit();
}else
{ //程序发生错误也需要关闭程序
exit();
}
$slice--;
} // 等待所有子进程结束后回收资源
while(!empty($childList)){
$childPid = pcntl_wait($status); if ($childPid > 0){ unset($childList[$childPid]);
}
}
}/** 运行的结果如下,phone不是连续的
Slice id:19,phone:66558
Slice id:23,phone:79921
Slice id:19,phone:66559
Slice id:23,phone:79922
Slice id:19,phone:66560
Slice id:23,phone:79923
Slice id:19,phone:66561
Slice id:23,phone:79924
Slice id:19,phone:66562
Slice id:23,phone:79925
**/
通过pcntl扩展,几句代码就使用多进程将发消息通知的功能提速了30倍。不过这么简单的多进程编码,我为什么会在文章开始形容的如此复杂呢?
重点和难点还是进程间通信,因为我们给用户发短信的每个子进程是相对独立的,进程之间没有通信,不会互相传递数据状态。所以不会发生资源抢占与锁问题。假如需求发生变化,我们需要按用户的活跃度高低给用户发短信,该怎么做?
通俗点解释如下:一个盘子里有30个苹果,需要发给30个人,由3个人负责发苹果。最简单的办法就是我们先把苹果分成3份,3个人一人一份,很快就能发完。但是如果我们要按照苹果的大小顺序去发,把大苹果先发出去,此时我们就没办法分成3份了,只能三个人互相去挣当前最大的,很容易就打起来。那该怎么做呢?最常见的办法就是使用一个工具把所有苹果按由大到下的顺序放在里面,每次只能取一个,这样就解决了资源抢占的问题。
关于进程间资源抢占的问题非常的复杂,编码难度非常高,这也是为什么很少使用PHP跑多进程的原因。当需要用到多进程时我们更愿意去使用Python或者Java,它们对多线程封装的更好。需要重点说的是PHP并不是不能写多进程的程序,也不是像其他人说的不稳定,而是编码费时,维护成本高。
二. 进程间通信
常见的进程通信方式有:消息队列、共享内存与信号量、管道、socket,我将一一举例说明。
消息队列
『消息队列』是在消息的传输过程中保存消息的容器。消息队列管理器相当于消息发送者和接收者的中介。消息队列的主要目的是创建路由并且保证消息可靠传递;如果发送消息时接收者不可用,消息队列会保留消息,直到有人接收它。
消息队列可提供临时存储的功能并且能保证消息可靠的传递,我们正好使用它实现进程间通信。当然消息队列不单单用于进程间通信,他的应用领域非常广。比如消息队列非常适用于解决消费者和生产者的问题,因为生产者和消费者之间总会存在『速度差』。比如生产者突然少了10个,两边处理的速度就会不平衡,会导致排队阻塞,服务不可用。这肯定不是我们想看到的,如果这时候引入消息队列将两个系统解耦,无论谁慢了都不会影响整体业务。
function demo(array $phoneList){ global $msgQueue;$cnt = count($phoneList); //测试数组大小
$slice = 3; //需要调用的进程数量
$childList = []; //主进程先发送一条消息,告诉子进程可以发送第一条短信了
msg_send($msgQueue,MSG_TYPE,0); while($slice >= 0)
{
$pid = pcntl_fork(); if($pid > 0){
$childList[$pid] = 1; //父进程什么都不用做
}elseif($pid == 0){ //子进程不停的请求,直到所有短信发送完成
while(msg_receive($msgQueue,MSG_TYPE,$msgType,1024,$message))
{ if($cnt>intval($message))
{
printf("Slice id:%s,phone:%s ",$slice,$phoneList[$message]);
$message = $message + 1;
msg_send($msgQueue,MSG_TYPE,$message);
}else
{ //通知其他进程一切都结束了
msg_send($msgQueue,MSG_TYPE,$cnt); exit();
}
}
}else
{ //程序发生错误也需要关闭程序
exit();
}
$slice--;
} // 等待所有子进程结束后回收资源
while(!empty($childList)){
$childPid = pcntl_wait($status); if ($childPid > 0){ unset($childList[$childPid]);
}
}
}const MSG_TYPE = 1;//创建消息队列$id = ftok(__FILE__,"m");
$msgQueue = msg_get_queue($id);
demo(range(0,900));/**运行结果,按大小输出
Slice id:1,phone:895
Slice id:1,phone:896
Slice id:2,phone:897
Slice id:3,phone:898
Slice id:3,phone:899
**/
共享内存与信号量
『共享内存』很容易理解,就是在内存中找一块区域,所有进程都能读写。『信号量』是系统提供的一种原子操作,进程在开启信号和结束信号之间拥有共享内存的『绝对占有』权,这样能有效的防止多个进程读取同一个资源时发生死锁。
function demo(array $phoneList){ global $shareMemory; global $signal;$cnt = count($phoneList); //测试数组大小
$slice = 3; //需要调用的进程数量
$childList = []; while($slice >= 0)
{
$pid = pcntl_fork(); if($pid > 0){
$childList[$pid] = 1; //父进程什么都不用做
}elseif($pid == 0){ while(true)
{ // 标记信号量,这里被我承包了
sem_acquire($signal); //检测共享内存是否存在
if (shm_has_var($shareMemory,SHARE_KEY)){ //从共享内存中拿数据
$val = shm_get_var($shareMemory,SHARE_KEY); if($val>=$cnt)
{
sem_release($signal); break;
}else
{
printf("Slice id:%s,phone:%s ",$slice,$phoneList[$val]);
$val ++; //再将数据写入共享内存
shm_put_var($shareMemory,SHARE_KEY,$val);
}
}else{ // 无值会,先初始化
shm_put_var($shareMemory,SHARE_KEY,0);
} // 用完释放
sem_release($signal);
} exit();
}else
{ //程序发生错误也需要关闭程序
exit();
}
$slice--;
} // 等待所有子进程结束后回收资源
while(!empty($childList)){
$childPid = pcntl_wait($status); if ($childPid > 0){ unset($childList[$childPid]);
}
}
}const SHARE_KEY = 1;// 创建一块共享内存$shm_id = ftok(__FILE__,"a");
$shareMemory = shm_attach($shm_id);// 创建一个信号量$sem_id = ftok(__FILE__,"b");
$signal = sem_get($sem_id);
demo(range(0,900));// 释放共享内存与信号量shm_remove($shareMemory);
sem_remove($signal);/**运行结果,按大小输出
Slice id:1,phone:775
Slice id:3,phone:776
Slice id:3,phone:777
Slice id:3,phone:778
Slice id:0,phone:779
Slice id:0,phone:780
**/
管道
管道是比较常用的进程间通信手段,管道又分为匿名管道(pipe)与具名管道(mkfifo),匿名管道只能用于具有亲缘关系的进程间通信,而具名管道可以用于同一主机上任意进程。
pipe与mkfifo的主要差别是mkfifo会创建一个特殊的FIFO物理文件,这个FIFO文件其他进程都可以像读写一般文件一样读写。再写下去文章就太长了,之后写下一篇吧。
未完待续……
PS:所有代码都放到了GitHub:php_thread_demo
沟通和互动以及更多干货,欢迎关注新浪微博:@阿里云云栖社区
在中国互联网行业膨胀的12-16年,PHP语言凭借自身易上手,开发周期短的优势,深受各中小企业的喜爱。小编也很荣幸在2009年底进入这一行业,为自己谋生分得一杯羹。但是PHP又先天有它的局限性,导致客户很多需求无法得到满足。大家都知道,P
Thinkphp:=> 简介:是由上海顶想公司开发的一款,PHP写成的,开源的MVC框架;官网:http://www.thinkphp.cn下载:https://github.com/top-think/framework 环境要求:
本文主要简析两个国产的PHP框架ThinkPHP与SpeedPHP。通过学习发现,它俩在很多方面有着相似但又不同的地方:1.单一入口,二者都是单一入口文件。每个app都需要一个入口文件,且只能有一个入口文件。ThinkPHP生而就有多app
PHP框架是什么?PHP框架提供了一个用以构建web应用的基本框架,从而简化了用PHP编写web应用程序的流程。这样不但节省开发时间,有助于建立更稳定的应用,而且减少了重复编码的开发。框架还可以帮助初学者建立更稳定的应用服务,这可以让你花更
截至目前(2014.2), PHP 的最新稳定版本是 PHP5.5, 但有差不多一半的用户仍在使用已经不在维护的 PHP5.2, 其余的一半用户在使用 PHP5.3。因为 PHP 那“集百家之长”的蛋疼语法,加上社区氛围不好,很多人对新版本
PHP进程探究PHP作为解释器运行通过线程或者进程都能实现(如果使用Apache,那么就可能使用多线程模型。使用php-fpm,就是使用多进程模型,这里以多进程模型解释)。服务器每接收到一个请求就要起一个PHP进程,平均一个PHP进程消耗内
ZWebPHP 框架设计目的:标准化、体验统一、简单可靠、易于扩展后端PHP框架基于PHP、smarty 构建。基于composer自动加载。完全基于API接口设计,API文档自动生成。权限包含菜单访问权限和API接口访问权限。工具类、数据
这也许,不,就是PHP世界中最好的日志组件—— Monolog所有的 PHPer,请站在巨人的肩膀上。随着 Composer 的普及,PHP 组件化开发思想越来越深入人心,我们没有必要重新自己打造轮子,只要确定需求和目标,设计好软件的架构,
PHP的功能越来越强大,里面有着非常丰富的内置函数。资深的PHP程序员对它们可能都很熟悉,但很多的PHP学习者,仍然对一些非常有用的函数不太熟悉。这篇文章里,我们就列举10个你或许不了解但实用的PHP函数,供大家参考和学习。1. php_c
Workerman是一款纯PHP开发的开源高性能的PHP socket 服务器框架。被广泛的用于手机app、移动通讯,微信小程序,手游服务端、网络游戏、PHP聊天室、硬件通讯、智能家居、车联网、物联网等领域的开发。 支持TCP长连接,支持W
框架是帮助初学者创建稳定的程序。使得你可以花更多的时间去创造真正的Web程序,而不是编写重复性的代码。框架第一阶段什么是框架?一直以来php框架被广泛利用,多半都是基本mvc架构模式的,国内外框架有HDPHP框架、ThinkPHP、CI框架
怎样搭建自己一个PHP框架?PHP写了5年,不想用其他框架,想自己搭建一套mvc oop的框架 可以用别人一些类库,但核心orm 之类希望自己写。这是最近遇到了别人的一个问题,5年时间的PHP资深程序是可以独立开发出自己框架。小编今天带来H
PHP的框架很多,有phalcon,kohana ,laravel,Yii,thinkPHP,ROR,HD,,CI,和ROR等,我觉得PHP框架其实本身就是一个工具而已,没有好与不好,只有合不合适。任何东西都没有最好,只是不同的取舍。有人说
ThinkPHP框架人们习惯性又叫它TP框架,是一个轻量级的国产PHP开发框架,快速、兼容而且简单, ThinkPHP可以支持windows/Unix/Linux等服务器环境,正式版需要PHP5.0以上版本支持,支持MySql、PgSQL、
PHP框架的发展背景毫无疑问,Web框架技术在近几年已经得到了突飞猛进的发展和普及,在过去几年里,框架技术的普遍经历了比较大的完善过程,很大一部分可以归因于RubyonRails,以及在其他编程语言中流露出的MVC框架思想。如果你是一个PH