前言
现在的工作中,经常要写一些脚本做一些异步的操作。

一般是大量的数据修改,或者解决部分并发问题。

为了能够稳定的做好数据处理,一般情况下会用定时脚本的方式。

那么问题来了。

可能存在的问题
当我们处理大量数据的时候,脚本的执行时间可能很长,或者重复处理某条数据(写错的情况下)。

为了避免数据的重复处理、运行脚本过多导致服务器压力过大等问题,我们需要限制脚本的运行数量。

如何做
思路一

查询某种标识的进程数量,如果超过一定数量,则直接退出,不处理。

思路二

记录每次的PID,可以使用 文件、redis、memcached 等来存储。

当启动一个新进程的时候,去查一下这个标识下面有哪些PID,是否还在运行,且与当前标识有关系。

当超过一定数量的时候,直接退出,不处理。

实践
思路一实践

这里通过 linux 的 ps、grep、wc 的命令来获取指定标识的运行进程数。
<?php
/**

  1. * 是否可以运行
  2. *
  3. * @param string $ident 标识
  4. * @param integer $maxNum 最大运行数量
  5. *
  6. * @return bool
  7. */
  8. function canRun($ident, $maxNum)
  9. {
  10. $cmd = sprintf('ps ax | grep %s | grep -v /bin/sh | grep -v grep | wc -l', $ident);
  11. $fp = @popen($cmd, 'r');
  12. $num = (int)trim(@fread($fp, 2096));
  13. @pclose($fp);
  14. return $num <= $maxNum;

思路二实践

这里使用 redis 存储 pid 信息。

通过 /proc/{pid}/cmdline 文件检测指定进程是否还在运行。
<?php
/**

  1. * 检查 pid 是否存活
  2. *
  3. * @param string $pid PID
  4. * @param string $ident 标识
  5. *
  6. * @return bool
  7. */
  8. function isSurvive($pid, $ident)
  9. {
  10. // 获取指定pid的cmdline文件
  11. $cmdlinePath = sprintf('/proc/%s/cmdline', $pid);
  12. if (!is_file($cmdlinePath)) {
  13. return false;
  14. }
  15. $cmdline = trim(file_get_contents($cmdlinePath));
  16. // 检查标识是否在 cmdline 中
  17. return strpos($cmdline, $ident) !== false;
  18. }
  19. /**
  20. * 是否可以运行
  21. *
  22. * @param string $ident 标识
  23. * @param integer $maxNum 最大运行数量
  24. *
  25. * @return bool
  26. */
  27. function canRun($ident, $maxNum)
  28. {
  29. // 假设已经链接上
  30. $redisHandler = getRedis();
  31. // 定义一个key
  32. $key = sprintf('php:job:%s:pid', $ident);
  33. // 当前的PID
  34. $currentPid = getmypid();
  35. // 将当前的PID写入redis
  36. $redis->sAdd($key, $currentPid);
  37. // 获取redis中的所有pid
  38. $pids = $redis->sMembers($key);
  39. // 遍历pid,检查是否有效
  40. foreach ($pids as $index => $pid) {
  41. if ($currentPid == $pid) {
  42. continue;
  43. }
  44. // 检查 pid 是否还在运行中
  45. if (isSurvive($pid, $ident)) {
  46. continue;
  47. }
  48. // 若不再运行,则直接删除
  49. unset($pids[$index]);
  50. $redis->sRemove($key, $pid);
  51. }
  52. return count($pids) <= $maxNum;
  53. }

关于标识
关于标识,可能我们在运行一些定时脚本的时候,统一的部分可能就是 php 了;或者,拥有相同标识的脚本,我们要归为几类。

为了能够实现这些需求,我们可以通过 php 的内置函数 cli_set_process_title 来实现自定义 COMMAND。
demo.php:

这个时候,我们运行 demo.php,然后通过 ps ax 可以看到如下结果:
PID USER TIME COMMAND
1 root 0:09 php-fpm: master process (/usr/local/etc/php-fpm.conf)
7 root 0:16 php-fpm: pool www
8 root 0:15 php-fpm: pool www
9 root 0:14 php-fpm: pool www
10 root 0:00 sh
663 root 0:00 sh
690 root 0:00 {php} Job Demo
691 root 0:00 ps ax

更多相关文章

  1. AndroidPN测试(Android(安卓)Push Notification)
  2. Android内存管理
  3. android 背景图片设置
  4. android 锁屏时,不运行锁屏程序
  5. android防止屏幕关闭后后台服务停止运行
  6. Your project contains error(s),please fix them before running
  7. android 实现home键效果
  8. Android(安卓)Activity属性介绍
  9. Android学习笔记(2):Hello world

随机推荐

  1. 深入理解Shadow DOM v1[每日前端夜话0x63
  2. 机器学习算法之线性回归的推导及应用
  3. 老司机带你了解微信/支付宝支付的相关概
  4. 如何使用 SSH 远程控制一台 Windows 服务
  5. 高效开发!借助 Mac + Windows 实现八屏办
  6. 200 行代码实现一个滑动验证码
  7. 架构设计|异步请求如何同步处理?
  8. 项目里文件名永远不要用中文!永远不要!
  9. 每日一技|活锁,也许你需要了解一下
  10. 深入剖析 RSA 密钥原理及实践