Php守护进程 异步处理和外部调用
看一下socket, 否则客户端刷新和服务端持续运行不知道是否保险. todo
php执行
- 是否能终止一个php文件的执行.
- 浏览器退出, 是否终止php执行.
- php 守护进程.
- php定时任务.
- php配合ajax和web socket.
php进程
- 在linux shell下执行php: php /data/htdocs/www/a.php
-
ps -ef grep “a.php”或者ps回车 - 定时任务: todo [https://segmentfault.com/a/1190000005879428]
最傻的和第二傻的
ignore_user_abort(); // 关掉浏览器,PHP脚本也可以继续执行.
set_time_limit(0); // 通过set_time_limit(0)可以让程序无限制的执行下去
ini_set('memory_limit','512M'); // 设置内存限制
while(true){ //这么搞, 会导致页面永远在加载中.
//ToDo
sleep(5);// 等待5秒
$run = include 'config.php';//迈向二傻, 这个php就是一个return.
if(!$run) die('process abort');//二傻
}
#config.php示例
<?
return 1;
?>
#要避免持续加载, 我们可以这样:
#fsockopen可以实现在请求访问某个文件时,不必获得返回结果就继续往下执行程序,这是和curl通常用法不一样的地方,我们在使用curl访问网页时,一定要等curl加载完网页后,才会执行curl后面的代码,虽然实际上curl也可以实现“非阻塞式”的请求,但是比fsockopen复杂的多,所以我们优先选择fsockopen,fsockopen可以在规定的时间内,比如1秒钟以内,完成对访问路径发出请求,完成之后就不管这个路径是否返回内容了,它的任务就到这里结束,可以继续往下执行程序了。利用这个特性,我们在正常的程序流中加入fsockopen,对上面我们创建的这个定时任务php的地址发出请求,即可让定时任务在后台执行。如果上面这个php的url地址是www.yourdomain.com/script.php,那么我们在编程中,可以这样:
// ...
// 正常的php执行程序
// ..
// 远程请求(不获取内容)函数,下面可以反复使用
function _sock($url) {
$host = parse_url($url,PHP_URL_HOST);
$port = parse_url($url,PHP_URL_PORT);
$port = $port ? $port : 80;
$scheme = parse_url($url,PHP_URL_SCHEME);
$path = parse_url($url,PHP_URL_PATH);
$query = parse_url($url,PHP_URL_QUERY);
if($query) $path .= '?'.$query;
if($scheme == 'https') {
$host = 'ssl://'.$host;
}
$fp = fsockopen($host,$port,$error_code,$error_msg,1);
if(!$fp) {
return array('error_code' => $error_code,'error_msg' => $error_msg);
}
else {
stream_set_blocking($fp,true);//开启了手册上说的非阻塞模式
stream_set_timeout($fp,1);//设置超时
$header = "GET $path HTTP/1.1\r\n";
$header.="Host: $host\r\n";
$header.="Connection: close\r\n\r\n";//长连接关闭
fwrite($fp, $header);
usleep(1000); // 这一句也是关键,如果没有这延时,可能在nginx服务器上就无法执行成功
其实比最傻还傻的是奇傻 - 看起来还挺聪明的, 实际巨傻
$url="http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
/*
function
*/
sleep(5); //等5秒
file_get_contents($url); // 这一句和下面一句, 二选一.
include(dirname(__FILE__).'/do.php'); //这一句和上面一句, 二选一.
// 这样干, 我的疑问是, 原始的这个进程会在什么时候终止? file_get_contents是异步的么?
// 而且不仅仅持续消耗内存, 并且还导致这个文件持续增大.
// 合乎逻辑的猜测是, 这个php比前面的大傻/二傻崩溃的还要快, 所以, 名之: 巨傻.
header("Location:http://www.manongjc.com");//header跳转之后再exit()或者die()
定时任务结合命令行执行脚本
- crontab可以做定时任务. 以分钟为单位.
- 因此要有一个死循环的php. 或者5s推算, 一分钟执行12次.
- 然后, 唤起新的php的时候, 杀死老的php. 那么要保证php操作的原子性. 或者说能够异步杀死老的php进程.
- 多个命令都可以, php, lynx -dump, curl, wget
感慨一下, 最终还是基础脚本, 想想也是, php其实并不依赖于nginx或者apache, 他只是和他们配合的比较好而已, php本身就是一个脚本. 理应能做所有脚本都可以做的事情.
js也是脚本, 因此才有浏览器里面运行的js, terminal运行的js, 以及server端运行的js.
现在真的是脚本的天下, 脚本可以作一切.
python, ruby, perl, php, js……
框架解决: swoole
http://wiki.swoole.com/wiki/page/1.html
阿里云定时任务
很好玩的是,一些服务商提供了各种类型的定时任务,例如阿里云的ACE提供了单独的定时任务,你可以填写自己应用下的某个uri。百度云BCE提供了服务器监测功能,每天会按照一定的时间规律访问应用下的固定uri。类似的第三方平台上还有很多定时任务可以用。你完全可以用这些第三方定时任务作为跳板,为你的网站定时任务服务。比如说,你可以在阿里云ACE上建立一个每天凌晨2点的定时任务,执行的uri是/cron.php。然后你创建一个cron.php,里面则采用fsockopen去访问你真正要执行某些任务的网站的url,例如上面的www.yourdomain.com/script.php,而且在cron.php中还可以访问多个url。然后把cron.php上传到你的ACE上面去,让ACE的定时任务去访问/cron.php,然后让cron.php去远程请求目标网站的定时任务脚本。
我不禁感慨: 这样也行? 早看到这个, 我就不去搞crontab了.
异步
- ajax/web socket
- popen 很快.
- curl, 最少要延迟一秒.
- fscokopen, 比较复杂. 而且进程太多, 就会挂掉.
- http://www.oschina.net/translate/how-to-make-async-requests-in-php
- https://www.pureweber.com/article/php-multi-process-programming-preview/
扩展:
- pcntl扩展:主要的进程扩展,完成进程创建于等待操作。
- posix扩展:完成posix兼容机通用api,如获取进程id,杀死进程等。
- sysvmsg扩展:实现system v方式的进程间通信之消息队列。
- sysvsem扩展:实现system v方式的信号量。
- sysvshm扩展:实现system v方式的共享内存。
- sockets扩展:实现socket通信。
进程间通信(IPC)通常linux中的进程通信方式有:
- 消息队列
- 信号量
- 共享内存
- 信号
- 管道
- socket
链接:http://www.jianshu.com/p/08bcf724196b
执行外部命令
- PHP提供共了3个专门的执行外部命令的函数:system(),exec(),passthru()。
- 用popen()函数打开进程
- 用反撇号 `,也就是键盘上ESC键下面的那个,和~在同一个上面.
$res=`./ls -l`;
echo ' '.$res.' '; # 这里就会显示打印bin目录的内容. 我亲测这个不可行, 没有执行结果.
pclose(popen("/usr/php /htd/task.php &", "r")); #这个比较帅, close和open在一行就调用好了.
file_get_contents: 把一个url的内容作为字符串获取.
停止 休眠
//根据网上资料,以下5行任何一行都能kill掉这个进程。 但是我被这个程序吓怕了,多带点符防鬼。
ignore_user_abort(false);
set_time_limit(10);
ob_end_flush();
echo "STOP";
exit(); //作用:输出该字符串后,立即停止php的执行!即后续程序不再执行,包括后续的其他所有php和html代码部分
die(字符串); exit(字符串); //exit是die的同义词,他们也可以不加字符串,而是直接停止.
sleep($n); //让程序停止休眠指定的秒数,然后等待过了那个时间后,就继续运行!
pcntl
有人用这个实现了守护进程, 而这个守护进程还是用了while(true), 所以依旧有一天停止的问题.
$pid = pcntl_fork(); //进程分叉?
if ($pid == -1) {
die('could not fork');
} else if ($pid) {//程序启动后,父进程会推出,
// we are the parent
//pcntl_wait($status); //Protect against Zombie children
exit($pid);
} else {//子进程会在后台运行,子进程权限从root切换到指定用户,同时将pid写入进程ID文件。
// we are the child
file_put_contents($this->pidfile, getmypid());
posix_setuid(self::uid);
posix_setgid(self::gid);
return(getmypid());
}
//程序停止,只需读取pid文件,然后调用posix_kill($pid, 9); 最后将该文件删除。
if (file_exists($this->pidfile)) {
$pid = file_get_contents($this->pidfile);
posix_kill($pid, 9);
unlink($this->pidfile);
}
# stop/start 或者 restart都会退出进程,重新启动,导致进程ID改变,同时瞬间退出导致业务闪断
# reload 实现原理是给进程发送SIGHUP信号,可以通过kill命令发送 kill -s SIGHUP 64881,也可以通过库函数实现 posix_kill(posix_getpid(), SIGUSR1);
结论
网上有大量的php实现守护进程的案例, 但是, 看上去都挺二的, 这本身不大像php该干的事, 我们最终在生产环境的解决方案还是依赖于shell脚本, crontab定期跑这个shell脚本, 在shell脚本里面我们结束老的php线程, 开启新的php线程.
- http://blog.csdn.net/tengzhaorong/article/details/9764655
# 使用nohup实现守护进程
nohup php myprog.php > log.txt &
#单独执行 php myprog.php,当按下ctrl+c时就会中断程序执行,会kill当前进程以及子进程。
#php myprog.php &,这样执行程序虽然也是转为后台运行,实际上是依赖终端的,当用户退出终端时进程就会被杀掉。
- http://rango.swoole.com/archives/59
- http://avnpc.com/pages/run-background-task-by-php-resque
- http://www.jb51.net/article/27593.htm
- http://blog.chinaunix.net/uid-27003384-id-3518925.html
- http://bayescafe.com/php/create-daemon-process-with-php.html
还有另一个思路: nodejs控制php进程
- http://taobaofed.org/blog/2015/11/24/nodejs-php-process-manager/
参考
- OSchina
- 非主流进程: http://blog.csdn.net/china_skag/article/details/7517021
- 守护进程: http://blog.csdn.net/tengzhaorong/article/details/9764655
- 糖霜: https://segmentfault.com/a/1190000002955509