SingleProcess.class.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <?php
  2. namespace KIF\Cli;
  3. use KIF\Verify;
  4. use Exception;
  5. use KIF\Core\Config;
  6. /**
  7. * 这是一个辅助性的东西,当你希望某个操作只运行一个进程的时候(例如生成缓存,既然生成缓存,几十个同时跑,我觉得还不如不要呢)
  8. * 还有像优享团结团后,发成功通知的短信,发了一次还发?sb了呢!
  9. *
  10. * @author gaoxiaogang@gmail.com
  11. *
  12. */
  13. class SingleProcess {
  14. /**
  15. * 唯一标识
  16. * @var string
  17. */
  18. private $pid;
  19. /**
  20. * 最大生存时间 单位:秒
  21. * @var int
  22. */
  23. private $timeout;
  24. /**
  25. * 是否检查操作系统进程
  26. * 该值为true时,将检查操作系统中的进程是否存在。如果不存在,返回已超时。
  27. * 只用于(unix/linux)
  28. * @var boolean
  29. */
  30. private $checkSystemProcess;
  31. public function __construct($pid, $timeout = 600, $checkSystemProcess = false) {
  32. if (!self::isValidPid($pid)) {
  33. throw new Exception('invalid $pid');
  34. }
  35. if (!Verify::int($timeout) || $timeout < 1) {
  36. throw new Exception('invalid $lifetime');
  37. }
  38. if (!is_bool($checkSystemProcess)) {
  39. throw new Exception('invalid $checkSystemProcess');
  40. }
  41. $this->pid = $pid;
  42. $this->timeout = (int) $timeout;
  43. $this->checkSystemProcess = $checkSystemProcess;
  44. }
  45. /**
  46. * 1、检测一个进程是否正在运行,如果返回true 那么就是正在运行,如果false 表示已经结束,你可以放心使用!
  47. * 2、当进程已经超时,也会返回false
  48. * 3、如果指定$this->checkSystemProcess为true,将检查操作系统中的进程是否存在。如果不存在,返回false。
  49. *
  50. * @return boolean
  51. */
  52. public function isRun() {
  53. $pf = $this->getPidPath();
  54. # 不存在
  55. if (!file_exists( $pf )) {
  56. return false;
  57. }
  58. # 超时了
  59. if ($this->isTimeout()) {
  60. $this->kill();
  61. return false;
  62. }
  63. # 系统进程不存在了,就认为前一个进程已经结束了。
  64. if ($this->checkSystemProcess) {
  65. $spid = $this->getSystemPid();
  66. if (!self::existSystemProcess($spid)) {
  67. return false;
  68. }
  69. }
  70. return true;
  71. }
  72. /**
  73. * 开始运行一个进程(声明:这只是一个虚拟的辅助操作,不要乱想)
  74. *
  75. * @return Boolean
  76. */
  77. public function run() {
  78. file_put_contents($this->getPidPath(), getmypid());
  79. return true;
  80. }
  81. /**
  82. * 激活
  83. * 如果new SignleProcess 时指定的timeout是600秒,当这个时间到达时,另一个进程就会将当前进程杀死。
  84. * 而active方法的作用,就是重新计算超时时间。
  85. * @param $activeFrequency 激活频率 300秒,即五分钟激活一次
  86. * @return boolean
  87. */
  88. public function active($activeFrequency = 300) {
  89. static $time_prev = null;
  90. if (is_null($time_prev)) {
  91. $time_prev = time();
  92. }
  93. $time_now = time();
  94. # 达到激活频率,声明脚本还活着
  95. if ($time_now - $time_prev >= $activeFrequency) {
  96. $time_prev = $time_now;//更新上次激活时间
  97. $this->run();
  98. return true;
  99. }
  100. return false;
  101. }
  102. /**
  103. * 进程结束
  104. *
  105. * @return Boolean
  106. */
  107. public function complete() {
  108. @unlink($this->getPidPath());
  109. return true;
  110. }
  111. /**
  112. * 终止一个进程
  113. *
  114. * @return Boolean
  115. */
  116. public function kill() {
  117. $pf = $this->getPidPath();
  118. if (!file_exists($pf)) {
  119. return true;
  120. }
  121. if ($this->checkSystemProcess) {
  122. $spid = $this->getSystemPid();
  123. if ($spid) {
  124. exec("kill -9 {$spid}");
  125. }
  126. }
  127. @unlink($pf);
  128. sleep(1);
  129. return true;
  130. }
  131. /**
  132. * 清除所有进程记录,不管有没结束
  133. *
  134. * @param boolean $checkSystemProcess 是否检查操作系统进程
  135. * @return Boolean
  136. */
  137. static public function cleanup($checkSystemProcess = false) {
  138. $path = self::getPath().'*';
  139. foreach (glob($path) as $filename) {
  140. if ($checkSystemProcess) {
  141. $spid = file_get_contents($filename);
  142. if (self::existSystemProcess($spid)) {
  143. exec("kill -9 {$spid}");//同时杀死该pid对应的操作系统进程
  144. }
  145. }
  146. @unlink($filename);
  147. }
  148. sleep(10);//休眠10秒
  149. return true;
  150. }
  151. /**
  152. * 取得缓存路径,如果不存在,自动创建
  153. *
  154. * @return String
  155. */
  156. static private function getPath() {
  157. # TODO 配置文件中 Log_Path
  158. $logPath = Config::getInstance ()->get ( 'Log_Path' );
  159. if(empty($logPath)){
  160. throw new Exception('Config Log_Path is empty');
  161. }
  162. $path = $logPath . DS . 'sspfiles' . DS;
  163. if (!file_exists($path)) {
  164. if (!mkdir($path, 0777, true)) {
  165. return false;
  166. }
  167. }
  168. return $path;
  169. }
  170. /**
  171. * 是否超时
  172. * @return Boolean
  173. */
  174. private function isTimeout() {
  175. if ((time() - filemtime($this->getPidPath())) > $this->timeout) {
  176. return true;
  177. }
  178. return false;
  179. }
  180. /**
  181. * 获取存放pid的全路径,包括pid文件名
  182. * @return string
  183. */
  184. private function getPidPath() {
  185. return self::getPath() . md5($this->pid);
  186. }
  187. /**
  188. * 获取系统进程pid
  189. * @return int | false
  190. */
  191. private function getSystemPid() {
  192. $spid = @file_get_contents($this->getPidPath());
  193. if ($spid) {
  194. return $spid;
  195. }
  196. return false;
  197. }
  198. /**
  199. * 是否存在操作系统进程
  200. *
  201. * @param int $spid 操作系统进程id
  202. * @return Boolean
  203. */
  204. static private function existSystemProcess($spid) {
  205. if (!Verify::int($spid) || $spid < 1) {
  206. return false;
  207. }
  208. $cmd = 'ps -eo pid | egrep ^\ *'.$spid.'$';
  209. $out = shell_exec($cmd);
  210. return empty($out) ? false : true;
  211. }
  212. /**
  213. * 是否有效的pid
  214. * @param string $pid
  215. * @return boolean
  216. */
  217. static private function isValidPid($pid) {
  218. if (!is_string($pid) || empty($pid)) {
  219. return false;
  220. }
  221. return true;
  222. }
  223. }