PHP daemon class

WBOY
Release: 2016-07-25 09:06:45
Original
838 people have browsed it
Daemon class implemented in PHP. You can implement queues or scheduled tasks out of crontab on the server.
When using it, inherit from this class, override the _doTask method, and initialize and execute it through main.
  1. class Daemon {
  2. const DLOG_TO_CONSOLE = 1;
  3. const DLOG_NOTICE = 2;
  4. const DLOG_WARNING = 4;
  5. const DLOG_ERROR = 8;
  6. const DLOG_CRITICAL = 16;
  7. const DAPC_PATH = '/tmp/daemon_apc_keys';
  8. /**
  9. * User ID
  10. *
  11. * @var int
  12. */
  13. public $userID = 65534; // nobody
  14. /**
  15. * Group ID
  16. *
  17. * @var integer
  18. */
  19. public $groupID = 65533; // nobody
  20. /**
  21. * Terminate daemon when set identity failure ?
  22. *
  23. * @var bool
  24. * @since 1.0.3
  25. */
  26. public $requireSetIdentity = false;
  27. /**
  28. * Path to PID file
  29. *
  30. * @var string
  31. * @since 1.0.1
  32. */
  33. public $pidFileLocation = '/tmp/daemon.pid';
  34. /**
  35. * processLocation
  36. * Process information record directory
  37. *
  38. * @var string
  39. */
  40. public $processLocation = '';
  41. /**
  42. * processHeartLocation
  43. * Process heartbeat packet file
  44. *
  45. * @var string
  46. */
  47. public $processHeartLocation = '';
  48. /**
  49. * Home path
  50. *
  51. * @var string
  52. * @since 1.0
  53. */
  54. public $homePath = '/';
  55. /**
  56. * Current process ID
  57. *
  58. * @var int
  59. * @since 1.0
  60. */
  61. protected $_pid = 0;
  62. /**
  63. * Is this process a children
  64. *
  65. * @var boolean
  66. * @since 1.0
  67. */
  68. protected $_isChildren = false;
  69. /**
  70. * Is daemon running
  71. *
  72. * @var boolean
  73. * @since 1.0
  74. */
  75. protected $_isRunning = false;
  76. /**
  77. * Constructor
  78. *
  79. * @return void
  80. */
  81. public function __construct() {
  82. error_reporting(0);
  83. set_time_limit(0);
  84. ob_implicit_flush();
  85. register_shutdown_function(array(&$this, 'releaseDaemon'));
  86. }
  87. /**
  88. * Start process
  89. *
  90. * @return bool
  91. */
  92. public function main() {
  93. $this->_logMessage('Starting daemon');
  94. if (!$this->_daemonize()) {
  95. $this->_logMessage('Could not start daemon', self::DLOG_ERROR);
  96. return false;
  97. }
  98. $this->_logMessage('Running...');
  99. $this->_isRunning = true;
  100. while ($this->_isRunning) {
  101. $this->_doTask();
  102. }
  103. return true;
  104. }
  105. /**
  106. * Stop the process
  107. *
  108. * @return void
  109. */
  110. public function stop() {
  111. $this->_logMessage('Stoping daemon');
  112. $this->_isRunning = false;
  113. }
  114. /**
  115. * Do task
  116. *
  117. * @return void
  118. */
  119. protected function _doTask() {
  120. // override this method
  121. }
  122. /**
  123. * _logMessage
  124. * Record log
  125. *
  126. * @param string message
  127. * @param integer level
  128. * @return void
  129. */
  130. protected function _logMessage($msg, $level = self::DLOG_NOTICE) {
  131. // override this method
  132. }
  133. /**
  134. * Daemonize
  135. *
  136. * Several rules or characteristics that most daemons possess:
  137. * 1) Check is daemon already running
  138. * 2) Fork child process
  139. * 3) Sets identity
  140. * 4) Make current process a session laeder
  141. * 5) Write process ID to file
  142. * 6) Change home path
  143. * 7) umask(0)
  144. *
  145. * @access private
  146. * @since 1.0
  147. * @return void
  148. */
  149. private function _daemonize() {
  150. ob_end_flush();
  151. if ($this->_isDaemonRunning()) {
  152. // Deamon is already running. Exiting
  153. return false;
  154. }
  155. if (!$this->_fork()) {
  156. // Coudn't fork. Exiting.
  157. return false;
  158. }
  159. if (!$this->_setIdentity() && $this->requireSetIdentity) {
  160. // Required identity set failed. Exiting
  161. return false;
  162. }
  163. if (!posix_setsid()) {
  164. $this->_logMessage('Could not make the current process a session leader', self::DLOG_ERROR);
  165. return false;
  166. }
  167. if (!$fp = fopen($this->pidFileLocation, 'w')) {
  168. $this->_logMessage('Could not write to PID file', self::DLOG_ERROR);
  169. return false;
  170. } else {
  171. fputs($fp, $this->_pid);
  172. fclose($fp);
  173. }
  174. // 写入监控日志
  175. $this->writeProcess();
  176. chdir($this->homePath);
  177. umask(0);
  178. declare(ticks = 1);
  179. pcntl_signal(SIGCHLD, array(&$this, 'sigHandler'));
  180. pcntl_signal(SIGTERM, array(&$this, 'sigHandler'));
  181. pcntl_signal(SIGUSR1, array(&$this, 'sigHandler'));
  182. pcntl_signal(SIGUSR2, array(&$this, 'sigHandler'));
  183. return true;
  184. }
  185. /**
  186. * Cheks is daemon already running
  187. *
  188. * @return bool
  189. */
  190. private function _isDaemonRunning() {
  191. $oldPid = file_get_contents($this->pidFileLocation);
  192. if ($oldPid !== false && posix_kill(trim($oldPid),0))
  193. {
  194. $this->_logMessage('Daemon already running with PID: '.$oldPid, (self::DLOG_TO_CONSOLE | self::DLOG_ERROR));
  195. return true;
  196. }
  197. else
  198. {
  199. return false;
  200. }
  201. }
  202. /**
  203. * Forks process
  204. *
  205. * @return bool
  206. */
  207. private function _fork() {
  208. $this->_logMessage('Forking...');
  209. $pid = pcntl_fork();
  210. if ($pid == -1) {
  211. // 出错
  212. $this->_logMessage('Could not fork', self::DLOG_ERROR);
  213. return false;
  214. } elseif ($pid) {
  215. // 父进程
  216. $this->_logMessage('Killing parent');
  217. exit();
  218. } else {
  219. // fork的子进程
  220. $this->_isChildren = true;
  221. $this->_pid = posix_getpid();
  222. return true;
  223. }
  224. }
  225. /**
  226. * Sets identity of a daemon and returns result
  227. *
  228. * @return bool
  229. */
  230. private function _setIdentity() {
  231. if (!posix_setgid($this->groupID) || !posix_setuid($this->userID))
  232. {
  233. $this->_logMessage('Could not set identity', self::DLOG_WARNING);
  234. return false;
  235. }
  236. else
  237. {
  238. return true;
  239. }
  240. }
  241. /**
  242. * Signals handler
  243. *
  244. * @access public
  245. * @since 1.0
  246. * @return void
  247. */
  248. public function sigHandler($sigNo) {
  249. switch ($sigNo)
  250. {
  251. case SIGTERM: // Shutdown
  252. $this->_logMessage('Shutdown signal');
  253. exit();
  254. break;
  255. case SIGCHLD: // Halt
  256. $this->_logMessage('Halt signal');
  257. while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
  258. break;
  259. case SIGUSR1: // User-defined
  260. $this->_logMessage('User-defined signal 1');
  261. $this->_sigHandlerUser1();
  262. break;
  263. case SIGUSR2: // User-defined
  264. $this->_logMessage('User-defined signal 2');
  265. $this->_sigHandlerUser2();
  266. break;
  267. }
  268. }
  269. /**
  270. * Signals handler: USR1
  271. * Mainly used to regularly clean up cached domain name dns resolution records in each process
  272. *
  273. * @return void
  274. */
  275. protected function _sigHandlerUser1() {
  276. apc_clear_cache('user');
  277. }
  278. /**
  279. * Signals handler: USR2
  280. * Used to write heartbeat packet files
  281. *
  282. * @return void
  283. */
  284. protected function _sigHandlerUser2() {
  285. $this->_initProcessLocation();
  286. file_put_contents($this->processHeartLocation, time());
  287. return true;
  288. }
  289. /**
  290. * Releases daemon pid file
  291. * This method is called on exit (destructor like)
  292. *
  293. * @return void
  294. */
  295. public function releaseDaemon() {
  296. if ($this->_isChildren && is_file($this->pidFileLocation)) {
  297. $this->_logMessage('Releasing daemon');
  298. unlink($this->pidFileLocation);
  299. }
  300. }
  301. /**
  302. * writeProcess
  303. * Write the current process information to the monitoring log. Another script will scan the data of the monitoring log and send a signal. If there is no response, restart the process
  304. *
  305. * @return void
  306. */
  307. public function writeProcess() {
  308. // Initialize proc
  309. $this->_initProcessLocation();
  310. $command = trim(implode(' ', $_SERVER['argv ']));
  311. // Specify the directory of the process
  312. $processDir = $this->processLocation . '/' . $this->_pid;
  313. $processCmdFile = $processDir . '/cmd';
  314. $processPwdFile = $processDir . '/pwd';
  315. // The directory where all processes are located
  316. if (!is_dir($this->processLocation)) {
  317. mkdir($this->processLocation, 0777);
  318. chmod($ processDir, 0777);
  319. }
  320. // Query duplicate process records
  321. $pDirObject = dir($this->processLocation);
  322. while ($pDirObject && (($pid = $pDirObject->read()) !== false)) {
  323. if ($pid == '.' || $pid == '..' || intval($pid) != $pid) {
  324. continue;
  325. }
  326. $pDir = $this->processLocation . '/' . $pid;
  327. $pCmdFile = $pDir . '/cmd';
  328. $pPwdFile = $pDir . '/pwd';
  329. $pHeartFile = $pDir . '/heart';
  330. // Start the process with the same parameters based on cmd check
  331. if (is_file($pCmdFile) && trim(file_get_contents($pCmdFile)) == $command) {
  332. unlink($pCmdFile);
  333. unlink($pPwdFile);
  334. unlink($pHeartFile);
  335. // Deleted directory has cache
  336. usleep(1000);
  337. rmdir($pDir);
  338. }
  339. }
  340. // New process directory
  341. if (!is_dir($processDir)) {
  342. mkdir($processDir, 0777);
  343. chmod($processDir, 0777);
  344. }
  345. // Write command parameters
  346. file_put_contents($processCmdFile, $command);
  347. file_put_contents($processPwdFile, $_SERVER['PWD' ]);
  348. // Writing files are cached
  349. usleep(1000);
  350. return true;
  351. }
  352. /**
  353. * _initProcessLocation
  354. * Initialization
  355. *
  356. * @return void
  357. */
  358. protected function _initProcessLocation() {
  359. $this->processLocation = ROOT_PATH . '/app/data/proc';
  360. $this->processHeartLocation = $this->processLocation . '/' . $this->_pid . '/heart';
  361. }
  362. }
Copy code


Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!