在传统的nginx+php-fpm模式中,每次请求结束后资源都会被释放,下次有新的请求会重新加载文件,所以只要更新了代码即可马上生效,但是在cli命令行模式开发中,开启的php进程服务一般都是守护进程,代码只在开启时进行加载,就算代码有更新也不会重新加载,直到进程结束都还是最开始加载的代码,导致每次更新代码都要重启php服务,这样的体验是非常不好的,我们可以借用swoole+Inotify来解决这个问题

Inotify介绍

inotify是Linux内核提供的一组系统调用,它可以监控文件系统操作,比如文件或者目录的创建、读取、写入、权限修改和删除等。

inotify使用也很简单,使用inotify_init创建一个句柄,然后通过inotify_add_watch/inotify_rm_watch增加/删除对文件和目录的监听。

PHP中提供了inotify扩展,支持了inotify系统调用。inotify本身也是一个文件描述符,可以加入到事件循环中,配合使用swoole扩展,就可以异步非阻塞地实时监听文件/目录变化

Inotif安装

1、可以使用pecl install inotify

2、(https://pecl.php.net/get/inotify-2.0.0.tgz)编译安装步骤跟之前一样,自行安装

在swoole中,我们可以向主进程发送各种不同的信号,主进程根据接收到的信号类型做出不同的处理。比如下面这几个

1、kill -SIGTERM|-15 master_pid  终止Swoole程序,一种优雅的终止信号,会待进程执行完当前程序之后中断,而不是直接干掉进程

2、kill -USR1|-10  master_pid  重启所有的Worker进程

3、kill -USR2|-12  master_pid  重启所有的Task Worker进程

当USR1信号被发送给Master进程后,Master进程会将同样的信号通过Manager进程转发Worker进程,收到此信号的Worker进程会在处理完正在执行的逻辑之后,释放进程内存,关闭自己,然后由Manager进程重启一个新的Worker进程。新的Worker进程会占用新的内存空间。

具体场景:

如果是上线的项目,一台繁忙的后端服务器随时都在处理请求,如果管理员通过kill进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止。

这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。

/*

*热重启

*/

class Worker{

//监听socket

protected $socket = NULL;

//连接事件回调

public $onConnect = NULL;

public  $reusePort=1;

//接收消息事件回调

public $onMessage = NULL;

public $workerNum=3; //子进程个数

public  $allSocket; //存放所有socket

public  $addr;

protected $worker_pid; //子进程pid

protected  $master_pid;//主进程id

public function __construct($socket_address) {

//监听地址+端口

$this->addr=$socket_address;

$this->master_pid=posix_getpid();

}

public function start() {

//获取配置文件

$this->watch();

$this->fork($this->workerNum);

$this->monitorWorkers(); //监视程序,捕获信号,监视worker进程

}

/**

* 文件监视,自动重启

*/

protected  function watch(){

$init=inotify_init(); //初始化

$files=get_included_files();

foreach ($files as $file){

inotify_add_watch($init,$file,IN_MODIFY); //监视相关的文件

}

//监听

swoole_event_add($init,function ($fd){

$events=inotify_read($fd);

if(!empty($events)){

posix_kill($this->master_pid,SIGUSR1);

}

});

}

/**

* 捕获信号

* 监视worker进程.拉起进程

*/

public  function monitorWorkers(){

//注册信号事件回调,是不会自动执行的

// reload

pcntl_signal(SIGUSR1, array($this, 'signalHandler'),false); //重启woker进程信号

//ctrl+c

$status=0;

while (1){

// 当发现信号队列,一旦发现有信号就会触发进程绑定事件回调

pcntl_signal_dispatch();

$pid = pcntl_wait($status); //当信号到达之后就会被中断

//如果进程不是正常情况下的退出,重启子进程,我想要维持子进程个数

//            if($pid>1 && $pid != $this->master_pid  && !pcntl_wifexited($status)){

//                    $index=array_search($pid,$this->worker_pid);

//                    $this->fork(1);

//                    var_dump('拉起子进程');

//                    unset($this->worker_pid[$index]);

//            }

pcntl_signal_dispatch();

//进程重启的过程当中会有新的信号过来,如果没有调用pcntl_signal_dispatch,信号不会被处理

}

}

public function signalHandler($sigo){

switch ($sigo){

case SIGUSR1:

$this->reload();

echo "收到重启信号";

break;

}

}

public function fork($worker_num){

for ($i=0;$i

$test=include 'index.php';

var_dump($test);

$pid=pcntl_fork(); //创建成功会返回子进程id

if($pid<0){

exit('创建失败');

}else if($pid>0){

//父进程空间,返回子进程id

$this->worker_pid[]=$pid;

}else{ //返回为0子进程空间

$this->accept();//子进程负责接收客户端请求

exit;

}

}

//放在父进程空间,结束的子进程信息,阻塞状态

}

public  function  accept(){

$opts = array(

'socket' => array(

'backlog' =>10240, //成功建立socket连接的等待个数

),

);

$context = stream_context_create($opts);

//开启多端口监听,并且实现负载均衡

stream_context_set_option($context,'socket','so_reuseport',1);

stream_context_set_option($context,'socket','so_reuseaddr',1);

$this->socket=stream_socket_server($this->addr,$errno,$errstr,STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,$context);

//第一个需要监听的事件(服务端socket的事件),一旦监听到可读事件之后会触发

swoole_event_add($this->socket,function ($fd){

$clientSocket=stream_socket_accept($fd);

//触发事件的连接的回调

if(!empty($clientSocket) && is_callable($this->onConnect)){

call_user_func($this->onConnect,$clientSocket);

}

//监听客户端可读

swoole_event_add($clientSocket,function ($fd){

//从连接当中读取客户端的内容

$buffer=fread($fd,1024);

//如果数据为空,或者为false,不是资源类型

if(empty($buffer)){

if(!is_resource($fd) || feof($fd) ){

//触发关闭事件

fclose($fd);

}

}

//正常读取到数据,触发消息接收事件,响应内容

if(!empty($buffer) && is_callable($this->onMessage)){

call_user_func($this->onMessage,$fd,$buffer);

}

});

});

}

/**

* 重启worker进程

*/

public  function reload(){

foreach ($this->worker_pid as $index=>$pid){

posix_kill($pid,SIGKILL); //结束进程

var_dump("杀掉的子进程",$pid);

unset($this->worker_pid[$index]);

$this->fork(1); //重新拉起worker

}

}

//捕获信号之后重启worker进程

}

//ps -ef | grep php | grep -v grep | awk '{print $2}' | xargs kill -s 9

$worker = new Worker('tcp://0.0.0.0:9800');

//开启多进程的端口监听

$worker->reusePort = true;

//连接事件

$worker->onConnect = function ($fd) {

//echo '连接事件触发',(int)$fd,PHP_EOL;

};

$worker->onTask = function ($fd) {

//echo '连接事件触发',(int)$fd,PHP_EOL;

};

//消息接收

$worker->onMessage = function ($conn, $message) {

//事件回调当中写业务逻辑

// $a=include 'index.php';

// var_dump($a);

//var_dump($conn,$message);

$content="我是peter";

$http_resonse = "HTTP/1.1 200 OK\r\n";

$http_resonse .= "Content-Type: text/html;charset=UTF-8\r\n";

$http_resonse .= "Connection: keep-alive\r\n"; //连接保持

$http_resonse .= "Server: php socket server\r\n";

$http_resonse .= "Content-length: ".strlen($content)."\r\n\r\n";

$http_resonse .= $content;

fwrite($conn, $http_resonse);

};

$worker->start(); //启动

php 监听用户退出,php 利用 Inotify监视程序 用于重启服务器进程相关推荐

  1. uniapp实战项目 (仿知识星球App) - - 利用computed监听用户操作

    实战项目名称:仿知识星球App 技术栈:前端 => uni-app ( 后端:Node.js + Mysql + Apollo + Graphql ) 已实现功能:微信登录,创建星球,内容管理, ...

  2. Python中用keyboard库实现监听用户输入

    Python中用keyboard库实现监听用户输入 前言 一.keyboard库 二.使用示例 1.引入库 前言 在用Python做一个Selenium网页自动化工具时,发现如果用户提供的数据太多,需 ...

  3. java edittext 输入监听_Android应用开发之Android EditText 监听用户输入完成的实例

    本文将带你了解Android应用开发Android EditText 监听用户输入完成的实例,希望本文对大家学Android有所帮助. 我们都知道, Android   EditText输入框,并没有 ...

  4. 微信被指监听用户,腾讯回应;谷歌意外推送 Android 11 Beta 更新;Linux 5.7 发布 | 极客头条...

    整理 | 屠敏 头图 | CSDN 下载自视觉中国 快来收听极客头条音频版吧,智能播报由出门问问「魔音工坊」提供技术支持. 「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客 ...

  5. 百度被告监听用户,回应“没那能力”,但小米说锅就是你的!

    点击上方"CSDN",选择"置顶公众号" 关键时刻,第一时间送达! 随便下载一款手机应用,开启时除了要求获取"位置权限"."存储权 ...

  6. JS监听用户按下ESC

    JS监听用户按下ESC $(document).ready(function(){}).keydown(function (e) {if (e.which === 27){console.log('按 ...

  7. 网传!微信监听用户聊天记录

    近日,网传"微信私下监听用户聊天记录,针对性推送朋友圈广告,引起了热议 了解到,有网友爆料称,微信会私下监听用户的聊天内容信息,然后针对性的推送聊天内容相关的广告信息,很多时候,前脚刚聊完, ...

  8. 监听用户在页面停留的时长 / 监听多个页面时长

    1.监听用户在页面停留的时长 2.如果要监听多个页面的时间,并且返回上一页累加上次浏览的时间功能 //记录阅读任务阅读列表recordTaskList() {this.quit_time = 0;le ...

  9. python全局键盘监听(pynput快捷键);利用pywin32快速截屏并生成视频

    python全局键盘监听(pynput快捷键):利用pywin32快速截屏并生成视频 第一次在CSDN写博客,有点小紧张(/ω\) 以下内容完全个人理解,有错误请指出~ 最近在用python做一个小工 ...

最新文章

  1. Excel VBA 教程
  2. Linux内核学习--内存管理模块
  3. Openssl asn1parse命令
  4. spark mongo java_java及spark2.X连接mongodb3.X单机或集群的方法(带认证及不带认证)...
  5. 使用google map实现周边搜索的功能_「转」“搜索”的原理,架构,实现,实践,面试不用再怕了...
  6. linux安装之后缺少命令,Centos 7 最小安装后关键命令找不到 ifconfig等
  7. oracle打开 txt文件,oracle 导入txt文件
  8. 第二篇:从 GPU 的角度理解并行计算
  9. 同济大学软件学院院长谈嵌入式方向选择
  10. Excel-财务函数1
  11. Win10切换虚拟桌面
  12. 高中数学记录日志(2019人教A版必修二)
  13. 阿里笔试--智能对话简化版之query指令槽位识别
  14. 怎么控制物联网卡的流量使用?
  15. mysql 与sqlserver对比?哪个更好用?
  16. 如何做到推广链接的落地页链接实现无变化跳转?
  17. java 调用打印机
  18. 间隙锁-记一次死锁原因分析
  19. element-plus组件默认英文的解决办法
  20. 强化学习matlab工具箱应用

热门文章

  1. 百度地图绘制途径点/分段绘制路线
  2. 头戴式蓝牙耳机哪款音质比较好?2020最受欢迎五款高清音质蓝牙耳机分享
  3. spring插件下载步骤
  4. 推荐Dropbox网盘
  5. 2016华为开发者大赛:赢的不仅仅是百万元奖金
  6. CUIT Online Judge 子段和
  7. Python提取通达信日数据转存为.csv表格数据
  8. 有哪些相见恨晚的高效学习方法?
  9. ICCV2017 论文浏览记录(转)
  10. C语言之洛谷刷题之路---顺序结构