看一下socket, 否则客户端刷新和服务端持续运行不知道是否保险. todo

php执行

  1. 是否能终止一个php文件的执行.
  2. 浏览器退出, 是否终止php执行.
  3. php 守护进程.
  4. php定时任务.
  5. php配合ajax和web socket.

php进程

  1. 在linux shell下执行php: php /data/htdocs/www/a.php
  2. ps -ef grep “a.php”或者ps回车
  3. 定时任务: 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()

定时任务结合命令行执行脚本

  1. crontab可以做定时任务. 以分钟为单位.
  2. 因此要有一个死循环的php. 或者5s推算, 一分钟执行12次.
  3. 然后, 唤起新的php的时候, 杀死老的php. 那么要保证php操作的原子性. 或者说能够异步杀死老的php进程.
  4. 多个命令都可以, 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了.

异步

  1. ajax/web socket
  2. popen 很快.
  3. curl, 最少要延迟一秒.
  4. 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

执行外部命令

  1. PHP提供共了3个专门的执行外部命令的函数:system(),exec(),passthru()。
  2. 用popen()函数打开进程
  3. 用反撇号 `,也就是键盘上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/

参考

  1. OSchina
  2. 非主流进程: http://blog.csdn.net/china_skag/article/details/7517021
  3. 守护进程: http://blog.csdn.net/tengzhaorong/article/details/9764655
  4. 糖霜: https://segmentfault.com/a/1190000002955509