Master.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. <?php
  2. namespace Man\Core;
  3. require_once WORKERMAN_ROOT_DIR . 'man/Core/Lib/Checker.php';
  4. require_once WORKERMAN_ROOT_DIR . 'man/Core/Lib/Config.php';
  5. require_once WORKERMAN_ROOT_DIR . 'man/Core/Lib/Task.php';
  6. require_once WORKERMAN_ROOT_DIR . 'man/Core/Lib/Log.php';
  7. if(!defined('WORKERMAN_ROOT_DIR'))
  8. {
  9. define('WORKERMAN_ROOT_DIR', realpath(__DIR__."/../../")."/");
  10. }
  11. /**
  12. *
  13. * 主进程
  14. *
  15. * @package Core
  16. *
  17. * @author walkor <worker-man@qq.com>
  18. * <b>使用示例:</b>
  19. * <pre>
  20. * <code>
  21. * Master::run();
  22. * <code>
  23. * </pre>
  24. *
  25. */
  26. class Master
  27. {
  28. /**
  29. * 版本
  30. * @var string
  31. */
  32. const VERSION = '2.0.1';
  33. /**
  34. * 服务名
  35. * @var string
  36. */
  37. const NAME = 'WorkerMan';
  38. /**
  39. * 服务状态 启动中
  40. * @var integer
  41. */
  42. const STATUS_STARTING = 1;
  43. /**
  44. * 服务状态 运行中
  45. * @var integer
  46. */
  47. const STATUS_RUNNING = 2;
  48. /**
  49. * 服务状态 关闭中
  50. * @var integer
  51. */
  52. const STATUS_SHUTDOWN = 4;
  53. /**
  54. * 服务状态 平滑重启中
  55. * @var integer
  56. */
  57. const STATUS_RESTARTING_WORKERS = 8;
  58. /**
  59. * 整个服务能够启动的最大进程数
  60. * @var integer
  61. */
  62. const SERVER_MAX_WORKER_COUNT = 5000;
  63. /**
  64. * 单个进程打开文件数限制
  65. * @var integer
  66. */
  67. const MIN_SOFT_OPEN_FILES = 10000;
  68. /**
  69. * 单个进程打开文件数限制 硬性限制
  70. * @var integer
  71. */
  72. const MIN_HARD_OPEN_FILES = 10000;
  73. /**
  74. * 共享内存中用于存储主进程统计信息的变量id
  75. * @var integer
  76. */
  77. const STATUS_VAR_ID = 1;
  78. /**
  79. * 发送停止命令多久后worker没退出则发送sigkill信号
  80. * @var integer
  81. */
  82. const KILL_WORKER_TIME_LONG = 4;
  83. /**
  84. * 用于保存所有子进程pid ['worker_name1'=>[pid1=>pid1,pid2=>pid2,..], 'worker_name2'=>[pid3,..], ...]
  85. * @var array
  86. */
  87. protected static $workerPids = array();
  88. /**
  89. * 服务的状态,默认是启动中
  90. * @var integer
  91. */
  92. protected static $serverStatus = self::STATUS_STARTING;
  93. /**
  94. * 用来监听端口的Socket数组,用来fork worker使用
  95. * @var array
  96. */
  97. protected static $listenedSockets = array();
  98. /**
  99. * 要重启的worker的pid数组 [pid1=>time_stamp, pid2=>time_stamp, ..]
  100. * @var array
  101. */
  102. protected static $workerToRestart = array();
  103. /**
  104. * 共享内存resource id
  105. * @var resource
  106. */
  107. protected static $shmId = 0;
  108. /**
  109. * 消息队列 resource id
  110. * @var resource
  111. */
  112. protected static $queueId = 0;
  113. /**
  114. * master进程pid
  115. * @var integer
  116. */
  117. protected static $masterPid = 0;
  118. /**
  119. * server统计信息 ['start_time'=>time_stamp, 'worker_exit_code'=>['worker_name1'=>[code1=>count1, code2=>count2,..], 'worker_name2'=>[code3=>count3,...], ..] ]
  120. * @var array
  121. */
  122. protected static $serverStatusInfo = array(
  123. 'start_time' => 0,
  124. 'worker_exit_code' => array(),
  125. );
  126. /**
  127. * 服务运行
  128. * @return void
  129. */
  130. public static function run()
  131. {
  132. // 输出信息
  133. self::notice("Server is starting ...", true);
  134. // 初始化
  135. self::init();
  136. // 检查环境
  137. self::checkEnv();
  138. // 变成守护进程
  139. self::daemonize();
  140. // 保存进程pid
  141. self::savePid();
  142. // 安装信号
  143. self::installSignal();
  144. // 创建监听套接字
  145. self::createSocketsAndListen();
  146. // 创建worker进程
  147. self::createWorkers();
  148. // 输出信息
  149. self::notice("Server start success ...", true);
  150. // 标记sever状态为运行中...
  151. self::$serverStatus = self::STATUS_RUNNING;
  152. // 关闭标准输出
  153. self::resetStdFd();
  154. // 主循环
  155. self::loop();
  156. }
  157. /**
  158. * 初始化 配置、进程名、共享内存、消息队列等
  159. * @return void
  160. */
  161. public static function init()
  162. {
  163. // 获取配置文件
  164. $config_path = Lib\Config::$filename;
  165. // 设置进程名称,如果支持的话
  166. self::setProcessTitle(self::NAME.':master with-config:' . $config_path);
  167. // 初始化共享内存消息队列
  168. if(extension_loaded('sysvmsg') && extension_loaded('sysvshm'))
  169. {
  170. self::$shmId = shm_attach(IPC_KEY, DEFAULT_SHM_SIZE, 0666);
  171. self::$queueId = msg_get_queue(IPC_KEY, 0666);
  172. msg_set_queue(self::$queueId,array('msg_qbytes'=>65535));
  173. }
  174. }
  175. /**
  176. * 检查环境配置
  177. * @return void
  178. */
  179. public static function checkEnv()
  180. {
  181. // 检查PID文件
  182. Lib\Checker::checkPidFile();
  183. // 检查扩展支持情况
  184. Lib\Checker::checkExtension();
  185. // 检查函数禁用情况
  186. Lib\Checker::checkDisableFunction();
  187. // 检查log目录是否可读
  188. Lib\Log::init();
  189. // 检查配置和语法错误等
  190. Lib\Checker::checkWorkersConfig();
  191. // 检查文件限制
  192. Lib\Checker::checkLimit();
  193. }
  194. /**
  195. * 使之脱离终端,变为守护进程
  196. * @return void
  197. */
  198. protected static function daemonize()
  199. {
  200. // 设置umask
  201. umask(0);
  202. // fork一次
  203. $pid = pcntl_fork();
  204. if(-1 == $pid)
  205. {
  206. // 出错退出
  207. exit("Daemonize fail ,can not fork");
  208. }
  209. elseif($pid > 0)
  210. {
  211. // 父进程,退出
  212. exit(0);
  213. }
  214. // 子进程使之成为session leader
  215. if(-1 == posix_setsid())
  216. {
  217. // 出错退出
  218. exit("Daemonize fail ,setsid fail");
  219. }
  220. // 再fork一次
  221. $pid2 = pcntl_fork();
  222. if(-1 == $pid2)
  223. {
  224. // 出错退出
  225. exit("Daemonize fail ,can not fork");
  226. }
  227. elseif(0 !== $pid2)
  228. {
  229. // 结束第一子进程,用来禁止进程重新打开控制终端
  230. exit(0);
  231. }
  232. // 记录server启动时间
  233. self::$serverStatusInfo['start_time'] = time();
  234. }
  235. /**
  236. * 保存主进程pid
  237. * @return void
  238. */
  239. public static function savePid()
  240. {
  241. // 保存在变量中
  242. self::$masterPid = posix_getpid();
  243. // 保存到文件中,用于实现停止、重启
  244. if(false === @file_put_contents(WORKERMAN_PID_FILE, self::$masterPid))
  245. {
  246. exit("\033[31;40mCan not save pid to pid-file(" . WORKERMAN_PID_FILE . ")\033[0m\n\n\033[31;40mServer start fail\033[0m\n\n");
  247. }
  248. // 更改权限
  249. chmod(WORKERMAN_PID_FILE, 0644);
  250. }
  251. /**
  252. * 获取主进程pid
  253. * @return int
  254. */
  255. public static function getMasterPid()
  256. {
  257. return self::$masterPid;
  258. }
  259. /**
  260. * 根据配置文件,创建监听套接字
  261. * @return void
  262. */
  263. protected static function createSocketsAndListen()
  264. {
  265. // 循环读取配置创建socket
  266. foreach (Lib\Config::getAllWorkers() as $worker_name=>$config)
  267. {
  268. if(isset($config['listen']))
  269. {
  270. $flags = substr($config['listen'], 0, 3) == 'udp' ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
  271. $error_no = 0;
  272. $error_msg = '';
  273. // 创建监听socket
  274. self::$listenedSockets[$worker_name] = stream_socket_server($config['listen'], $error_no, $error_msg, $flags);
  275. if(!self::$listenedSockets[$worker_name])
  276. {
  277. Lib\Log::add("can not create socket {$config['listen']} info:{$error_no} {$error_msg}\tServer start fail");
  278. exit("\n\033[31;40mcan not create socket {{$config['listen']} info:{$error_no} {$error_msg}\033[0m\n\n\033[31;40mServer start fail\033[0m\n\n");
  279. }
  280. }
  281. }
  282. }
  283. /**
  284. * 根据配置文件创建Workers
  285. * @return void
  286. */
  287. protected static function createWorkers()
  288. {
  289. // 循环读取配置创建一定量的worker进程
  290. foreach (Lib\Config::getAllWorkers() as $worker_name=>$config)
  291. {
  292. // 初始化
  293. if(empty(self::$workerPids[$worker_name]))
  294. {
  295. self::$workerPids[$worker_name] = array();
  296. }
  297. while(count(self::$workerPids[$worker_name]) < $config['start_workers'])
  298. {
  299. $pid = self::forkOneWorker($worker_name);
  300. // 子进程退出
  301. if($pid == 0)
  302. {
  303. self::notice("CHILD EXIT ERR");
  304. }
  305. }
  306. }
  307. }
  308. /**
  309. * 创建一个worker进程
  310. * @param string $worker_name worker的名称
  311. * @return int 父进程:>0得到新worker的pid ;<0 出错; 子进程:始终为0
  312. */
  313. protected static function forkOneWorker($worker_name)
  314. {
  315. // 创建子进程
  316. $pid = pcntl_fork();
  317. // 先处理收到的信号
  318. pcntl_signal_dispatch();
  319. // 父进程
  320. if($pid > 0)
  321. {
  322. // 初始化master的一些东东
  323. self::$workerPids[$worker_name][$pid] = $pid;
  324. // 更新进程信息到共享内存
  325. self::updateStatusToShm();
  326. return $pid;
  327. }
  328. // 子进程
  329. elseif($pid === 0)
  330. {
  331. // 忽略信号
  332. self::ignoreSignal();
  333. // 清空任务
  334. Lib\Task::delAll();
  335. // 关闭不用的监听socket
  336. foreach(self::$listenedSockets as $tmp_worker_name => $tmp_socket)
  337. {
  338. if($tmp_worker_name != $worker_name)
  339. {
  340. fclose($tmp_socket);
  341. }
  342. }
  343. // 尝试以指定用户运行worker
  344. if($worker_user = Lib\Config::get($worker_name . '.user'))
  345. {
  346. self::setWorkerUser($worker_user);
  347. }
  348. // 关闭输出
  349. self::resetStdFd();
  350. // 尝试设置子进程进程名称
  351. self::setWorkerProcessTitle($worker_name);
  352. // 查找worker文件
  353. if($worker_file = \Man\Core\Lib\Config::get($worker_name.'.worker_file'))
  354. {
  355. include_once $worker_file;
  356. $class_name = basename($worker_file, '.php');
  357. }
  358. else
  359. {
  360. $class_name = $worker_name;
  361. include_once WORKERMAN_ROOT_DIR . "workers/$worker_name.php";
  362. }
  363. // 创建实例
  364. $worker = new $class_name($worker_name);
  365. // 如果该worker有配置监听端口,则将监听端口的socket传递给子进程
  366. if(isset(self::$listenedSockets[$worker_name]))
  367. {
  368. $worker->setListendSocket(self::$listenedSockets[$worker_name]);
  369. }
  370. // 使worker开始服务
  371. $worker->start();
  372. return 0;
  373. }
  374. // 出错
  375. else
  376. {
  377. self::notice("create worker fail worker_name:$worker_name detail:pcntl_fork fail");
  378. return $pid;
  379. }
  380. }
  381. /**
  382. * 安装相关信号控制器
  383. * @return void
  384. */
  385. protected static function installSignal()
  386. {
  387. // 设置终止信号处理函数
  388. pcntl_signal(SIGINT, array('\Man\Core\Master', 'signalHandler'), false);
  389. // 设置SIGUSR1信号处理函数,测试用
  390. pcntl_signal(SIGUSR1, array('\Man\Core\Master', 'signalHandler'), false);
  391. // 设置SIGUSR2信号处理函数,平滑重启Server
  392. pcntl_signal(SIGHUP, array('\Man\Core\Master', 'signalHandler'), false);
  393. // 设置子进程退出信号处理函数
  394. pcntl_signal(SIGCHLD, array('\Man\Core\Master', 'signalHandler'), false);
  395. // 设置忽略信号
  396. pcntl_signal(SIGPIPE, SIG_IGN);
  397. pcntl_signal(SIGTTIN, SIG_IGN);
  398. pcntl_signal(SIGTTOU, SIG_IGN);
  399. pcntl_signal(SIGQUIT, SIG_IGN);
  400. pcntl_signal(SIGALRM, SIG_IGN);
  401. }
  402. /**
  403. * 忽略信号
  404. * @return void
  405. */
  406. protected static function ignoreSignal()
  407. {
  408. // 设置忽略信号
  409. pcntl_signal(SIGPIPE, SIG_IGN);
  410. pcntl_signal(SIGTTIN, SIG_IGN);
  411. pcntl_signal(SIGTTOU, SIG_IGN);
  412. pcntl_signal(SIGQUIT, SIG_IGN);
  413. pcntl_signal(SIGALRM, SIG_IGN);
  414. pcntl_signal(SIGINT, SIG_IGN);
  415. pcntl_signal(SIGUSR1, SIG_IGN);
  416. pcntl_signal(SIGHUP, SIG_IGN);
  417. pcntl_signal(SIGCHLD, SIG_IGN);
  418. }
  419. /**
  420. * 设置server信号处理函数
  421. * @param null $null
  422. * @param int $signal
  423. * @return void
  424. */
  425. public static function signalHandler($signal)
  426. {
  427. switch($signal)
  428. {
  429. // 停止server信号
  430. case SIGINT:
  431. self::notice("Server is shutting down");
  432. self::stop();
  433. break;
  434. // 测试用
  435. case SIGUSR1:
  436. break;
  437. // worker退出信号
  438. case SIGCHLD:
  439. // 不要在这里fork,fork出来的子进程无法收到信号
  440. // self::checkWorkerExit();
  441. break;
  442. // 平滑重启server信号
  443. case SIGHUP:
  444. Lib\Config::reload();
  445. self::notice("Server reloading");
  446. self::addToRestartWorkers(array_keys(self::getPidWorkerNameMap()));
  447. self::restartWorkers();
  448. break;
  449. }
  450. }
  451. /**
  452. * 设置子进程进程名称
  453. * @param string $worker_name
  454. * @return void
  455. */
  456. public static function setWorkerProcessTitle($worker_name)
  457. {
  458. if(isset(self::$listenedSockets[$worker_name]))
  459. {
  460. // 获得socket的信息
  461. $sock_name = stream_socket_get_name(self::$listenedSockets[$worker_name], false);
  462. // 更改进程名,如果支持的话
  463. $mata_data = stream_get_meta_data(self::$listenedSockets[$worker_name]);
  464. $protocol = substr($mata_data['stream_type'], 0, 3);
  465. self::setProcessTitle(self::NAME.":worker $worker_name {$protocol}://$sock_name");
  466. }
  467. else
  468. {
  469. self::setProcessTitle(self::NAME.":worker $worker_name");
  470. }
  471. }
  472. /**
  473. * 主进程主循环 主要是监听子进程退出、服务终止、平滑重启信号
  474. * @return void
  475. */
  476. public static function loop()
  477. {
  478. $siginfo = array();
  479. while(1)
  480. {
  481. @pcntl_sigtimedwait(array(SIGCHLD), $siginfo, 1);
  482. // 初始化任务系统
  483. Lib\Task::tick();
  484. // 检查是否有进程退出
  485. self::checkWorkerExit();
  486. // 触发信号处理
  487. pcntl_signal_dispatch();
  488. }
  489. }
  490. /**
  491. * 监控worker进程状态,退出重启
  492. * @param resource $channel
  493. * @param int $flag
  494. * @param int $pid 退出的进程id
  495. * @return mixed
  496. */
  497. public static function checkWorkerExit()
  498. {
  499. // 由于SIGCHLD信号可能重叠导致信号丢失,所以这里要循环获取所有退出的进程id
  500. while(($pid = pcntl_waitpid(-1, $status, WUNTRACED | WNOHANG)) != 0)
  501. {
  502. // 如果是重启的进程,则继续重启进程
  503. if(isset(self::$workerToRestart[$pid]) && self::$serverStatus != self::STATUS_SHUTDOWN)
  504. {
  505. unset(self::$workerToRestart[$pid]);
  506. self::restartWorkers();
  507. }
  508. // 出错
  509. if($pid == -1)
  510. {
  511. // 没有子进程了,可能是出现Fatal Err 了
  512. if(pcntl_get_last_error() == 10)
  513. {
  514. self::notice('Server has no workers now');
  515. }
  516. return -1;
  517. }
  518. // 查找子进程对应的woker_name
  519. $pid_workname_map = self::getPidWorkerNameMap();
  520. $worker_name = isset($pid_workname_map[$pid]) ? $pid_workname_map[$pid] : '';
  521. // 没找到worker_name说明出错了 哪里来的野孩子?
  522. if(empty($worker_name))
  523. {
  524. self::notice("child exist but not found worker_name pid:$pid");
  525. break;
  526. }
  527. // 进程退出状态不是0,说明有问题了
  528. if($status !== 0)
  529. {
  530. self::notice("worker exit status $status pid:$pid worker:$worker_name");
  531. }
  532. // 记录进程退出状态
  533. self::$serverStatusInfo['worker_exit_code'][$worker_name][$status] = isset(self::$serverStatusInfo['worker_exit_code'][$worker_name][$status]) ? self::$serverStatusInfo['worker_exit_code'][$worker_name][$status] + 1 : 1;
  534. // 更新状态到共享内存
  535. self::updateStatusToShm();
  536. // 清理这个进程的数据
  537. self::clearWorker($worker_name, $pid);
  538. // 如果服务是不是关闭中
  539. if(self::$serverStatus != self::STATUS_SHUTDOWN)
  540. {
  541. // 重新创建worker
  542. self::createWorkers();
  543. }
  544. // 判断是否都重启完毕
  545. else
  546. {
  547. $all_worker_pid = self::getPidWorkerNameMap();
  548. if(empty($all_worker_pid))
  549. {
  550. // 删除共享内存
  551. self::removeShmAndQueue();
  552. // 发送提示
  553. self::notice("Server stoped");
  554. // 删除pid文件
  555. @unlink(WORKERMAN_PID_FILE);
  556. exit(0);
  557. }
  558. }//end if
  559. }//end while
  560. }
  561. /**
  562. * 获取pid 到 worker_name 的映射
  563. * @return array ['pid1'=>'worker_name1','pid2'=>'worker_name2', ...]
  564. */
  565. public static function getPidWorkerNameMap()
  566. {
  567. $all_pid = array();
  568. foreach(self::$workerPids as $worker_name=>$pid_array)
  569. {
  570. foreach($pid_array as $pid)
  571. {
  572. $all_pid[$pid] = $worker_name;
  573. }
  574. }
  575. return $all_pid;
  576. }
  577. /**
  578. * 放入重启队列中
  579. * @param array $restart_pids
  580. * @return void
  581. */
  582. public static function addToRestartWorkers($restart_pids)
  583. {
  584. if(!is_array($restart_pids))
  585. {
  586. self::notice("addToRestartWorkers(".var_export($restart_pids, true).") \$restart_pids not array");
  587. return false;
  588. }
  589. // 将pid放入重启队列
  590. foreach($restart_pids as $pid)
  591. {
  592. if(!isset(self::$workerToRestart[$pid]))
  593. {
  594. // 重启时间=0
  595. self::$workerToRestart[$pid] = 0;
  596. }
  597. }
  598. }
  599. /**
  600. * 重启workers
  601. * @return void
  602. */
  603. public static function restartWorkers()
  604. {
  605. // 标记server状态
  606. if(self::$serverStatus != self::STATUS_RESTARTING_WORKERS && self::$serverStatus != self::STATUS_SHUTDOWN)
  607. {
  608. self::$serverStatus = self::STATUS_RESTARTING_WORKERS;
  609. }
  610. // 没有要重启的进程了
  611. if(empty(self::$workerToRestart))
  612. {
  613. self::$serverStatus = self::STATUS_RUNNING;
  614. self::notice("\nWorker Restart Success");
  615. return true;
  616. }
  617. // 遍历要重启的进程 标记它们重启时间
  618. foreach(self::$workerToRestart as $pid => $stop_time)
  619. {
  620. if($stop_time == 0)
  621. {
  622. self::$workerToRestart[$pid] = time();
  623. posix_kill($pid, SIGHUP);
  624. Lib\Task::add(self::KILL_WORKER_TIME_LONG, array('\Man\Core\Master', 'forceKillWorker'), array($pid), false);
  625. break;
  626. }
  627. }
  628. }
  629. /**
  630. * worker进程退出时,master进程的一些清理工作
  631. * @param string $worker_name
  632. * @param int $pid
  633. * @return void
  634. */
  635. protected static function clearWorker($worker_name, $pid)
  636. {
  637. // 释放一些不用了的数据
  638. unset(self::$workerToRestart[$pid], self::$workerPids[$worker_name][$pid]);
  639. }
  640. /**
  641. * 停止服务
  642. * @return void
  643. */
  644. public static function stop()
  645. {
  646. // 如果没有子进程则直接退出
  647. $all_worker_pid = self::getPidWorkerNameMap();
  648. if(empty($all_worker_pid))
  649. {
  650. exit(0);
  651. }
  652. // 标记server开始关闭
  653. self::$serverStatus = self::STATUS_SHUTDOWN;
  654. // killWorkerTimeLong 秒后如果还没停止则强制杀死所有进程
  655. Lib\Task::add(self::KILL_WORKER_TIME_LONG, array('\Man\Core\Master', 'stopAllWorker'), array(true), false);
  656. // 停止所有worker
  657. self::stopAllWorker();
  658. }
  659. /**
  660. * 停止所有worker
  661. * @param bool $force 是否强制退出
  662. * @return void
  663. */
  664. public static function stopAllWorker($force = false)
  665. {
  666. // 获得所有pid
  667. $all_worker_pid = self::getPidWorkerNameMap();
  668. // 强行杀死?
  669. if($force)
  670. {
  671. // 杀死所有子进程
  672. foreach($all_worker_pid as $pid=>$worker_name)
  673. {
  674. // 发送kill信号
  675. self::forceKillWorker($pid);
  676. self::notice("Kill workers($worker_name) force!");
  677. }
  678. }
  679. else
  680. {
  681. // 向所有子进程发送终止信号
  682. foreach($all_worker_pid as $pid=>$worker_name)
  683. {
  684. // 发送SIGINT信号
  685. posix_kill($pid, SIGINT);
  686. }
  687. }
  688. }
  689. /**
  690. * 强制杀死进程
  691. * @param int $pid
  692. * @return void
  693. */
  694. public static function forceKillWorker($pid)
  695. {
  696. if(posix_kill($pid, 0))
  697. {
  698. self::notice("Kill workers $pid force!");
  699. posix_kill($pid, SIGKILL);
  700. }
  701. }
  702. /**
  703. * 设置运行用户
  704. * @param string $worker_user
  705. * @return void
  706. */
  707. protected static function setWorkerUser($worker_user)
  708. {
  709. $user_info = posix_getpwnam($worker_user);
  710. // 尝试设置gid uid
  711. if(!posix_setgid($user_info['gid']) || !posix_setuid($user_info['uid']))
  712. {
  713. $notice = 'Notice : Can not run woker as '.$worker_user." , You shuld be root\n";
  714. self::notice($notice, true);
  715. }
  716. }
  717. /**
  718. * 获取共享内存资源id
  719. * @return resource
  720. */
  721. public static function getShmId()
  722. {
  723. return self::$shmId;
  724. }
  725. /**
  726. * 获取消息队列资源id
  727. * @return resource
  728. */
  729. public static function getQueueId()
  730. {
  731. return self::$queueId;
  732. }
  733. /**
  734. * 关闭标准输入输出
  735. * @return void
  736. */
  737. protected static function resetStdFd()
  738. {
  739. // 开发环境不关闭标准输出,用于调试
  740. if(Lib\Config::get('workerman.debug') == 1 && posix_ttyname(STDOUT))
  741. {
  742. return;
  743. }
  744. global $STDOUT, $STDERR;
  745. @fclose(STDOUT);
  746. @fclose(STDERR);
  747. // 将标准输出重定向到/dev/null
  748. $STDOUT = fopen('/dev/null',"rw+");
  749. $STDERR = fopen('/dev/null',"rw+");
  750. }
  751. /**
  752. * 更新主进程收集的状态信息到共享内存
  753. * @return bool
  754. */
  755. protected static function updateStatusToShm()
  756. {
  757. if(!self::$shmId)
  758. {
  759. return true;
  760. }
  761. return shm_put_var(self::$shmId, self::STATUS_VAR_ID, array_merge(self::$serverStatusInfo, array('pid_map'=>self::$workerPids)));
  762. }
  763. /**
  764. * 销毁共享内存以及消息队列
  765. * @return void
  766. */
  767. protected static function removeShmAndQueue()
  768. {
  769. if(self::$shmId)
  770. {
  771. shm_remove(self::$shmId);
  772. }
  773. if(self::$queueId)
  774. {
  775. msg_remove_queue(self::$queueId);
  776. }
  777. }
  778. /**
  779. * 设置进程名称,需要proctitle支持 或者php>=5.5
  780. * @param string $title
  781. * @return void
  782. */
  783. protected static function setProcessTitle($title)
  784. {
  785. // >=php 5.5
  786. if (version_compare(phpversion(), "5.5", "ge") && function_exists('cli_set_process_title'))
  787. {
  788. cli_set_process_title($title);
  789. }
  790. // 需要扩展
  791. elseif(extension_loaded('proctitle') && function_exists('setproctitle'))
  792. {
  793. setproctitle($title);
  794. }
  795. }
  796. /**
  797. * notice,记录到日志
  798. * @param string $msg
  799. * @param bool $display
  800. * @return void
  801. */
  802. public static function notice($msg, $display = false)
  803. {
  804. Lib\Log::add("Server:".$msg);
  805. if($display)
  806. {
  807. if(self::$serverStatus == self::STATUS_STARTING)
  808. {
  809. echo($msg."\n");
  810. }
  811. }
  812. }
  813. }