1010
1111use inhere \console \io \Input ;
1212use inhere \console \io \Output ;
13+ use inhere \console \utils \Show ;
1314use inhere \library \helpers \PhpHelper ;
1415use inhere \library \helpers \ProcessHelper ;
1516use inhere \library \traits \EventTrait ;
@@ -62,13 +63,16 @@ class Application
6263 use EventTrait;
6364
6465 // some events
65- const ON_WS_CONNECT = 'wsConnect ' ;
66- const ON_WS_OPEN = 'wsOpen ' ;
67- const ON_WS_MESSAGE = 'wsMessage ' ;
68- const ON_WS_CLOSE = 'wsClose ' ;
69- const ON_WS_ERROR = 'wsError ' ;
70- const ON_NO_MODULE = 'noModule ' ;
71- const ON_PARSE_ERROR = 'parseError ' ;
66+ const EVT_WS_CONNECT = 'wsConnect ' ;
67+ const EVT_WS_OPEN = 'wsOpen ' ;
68+ const EVT_WS_DISCONNECT = 'wsDisconnect ' ;
69+ const EVT_HANDSHAKE_REQUEST = 'handshakeRequest ' ;
70+ const EVT_HANDSHAKE_SUCCESSFUL = 'handshakeSuccessful ' ;
71+ const EVT_WS_MESSAGE = 'wsMessage ' ;
72+ const EVT_WS_CLOSE = 'wsClose ' ;
73+ const EVT_WS_ERROR = 'wsError ' ;
74+ const EVT_NO_MODULE = 'noModule ' ;
75+ const EVT_PARSE_ERROR = 'parseError ' ;
7276
7377 // custom ws handler position
7478 const OPEN_HANDLER = 0 ;
@@ -162,6 +166,10 @@ class Application
162166 */
163167 private $ modules ;
164168
169+ private $ pidFile ;
170+
171+ private $ bootstrapped = false ;
172+
165173 /**
166174 * Application constructor.
167175 * @param string $host
@@ -258,7 +266,7 @@ protected function handleCliCommand()
258266 break ;
259267 default :
260268 $ this ->cliOut ->error ("The command [ {$ command }] is don't supported! " );
261- $ this ->showHelpInfo ();
269+ $ this ->help ();
262270 break ;
263271 }
264272
@@ -306,7 +314,7 @@ protected function checkInputCommand($command)
306314 }
307315 }
308316
309- public function start ( $ daemon = null )
317+ public function bootstrap ( )
310318 {
311319 // prepare server instance
312320 $ this ->prepareServer ();
@@ -318,7 +326,7 @@ public function start($daemon = null)
318326 $ this ->ws ->on (WSInterface::ON_CLOSE , [$ this , 'handleClose ' ]);
319327 $ this ->ws ->on (WSInterface::ON_ERROR , [$ this , 'handleError ' ]);
320328
321- // if not register route, add root path route handler
329+ // if not register route, add a default root path module handler
322330 if (0 === count ($ this ->modules )) {
323331 $ this ->module ('/ ' , new RootModule );
324332 }
@@ -327,46 +335,188 @@ public function start($daemon = null)
327335 $ this ->ws ->start ();
328336 }
329337
330- public function restart ($ daemon = null )
338+ /**
339+ * @param bool $value
340+ * @return $this
341+ */
342+ public function asDaemon ($ value = true )
331343 {
332- // prepare server instance
333- $ this ->prepareServer ();
344+ $ this ->daemon = (bool )$ value ;
345+ $ this ->config ['swoole ' ]['daemonize ' ] = (bool )$ value ;
346+
347+ return $ this ;
348+ }
349+
350+ /**
351+ * Do start server
352+ * @param null|bool $daemon
353+ */
354+ public function start ($ daemon = null )
355+ {
356+ if ($ pid = $ this ->getPidFromFile (true )) {
357+ Show::error ("The swoole server( {$ this ->name }) have been started. (PID: {$ pid }) " , -1 );
358+ }
334359
335- if ($ this -> ws -> isRunning () ) {
336- $ this ->stop ( );
360+ if (null !== $ daemon ) {
361+ $ this ->asDaemon ( $ daemon );
337362 }
338363
339- $ this ->start ($ daemon );
364+ if (!$ this ->bootstrapped ) {
365+ $ this ->bootstrap ();
366+ }
367+
368+ self ::$ _statistics ['start_time ' ] = microtime (1 );
369+
370+ $ this ->beforeServerStart ();
371+
372+ $ this ->server ->start ();
373+ }
374+
375+ /**
376+ * before Server Start
377+ */
378+ public function beforeServerStart ()
379+ {
340380 }
341381
342- public function reload ($ onlyTask = false )
382+ /**
383+ * do Reload Workers
384+ * @param boolean $onlyTaskWorker
385+ * @return int
386+ */
387+ public function reload ($ onlyTaskWorker = false )
343388 {
389+ if (!$ masterPid = $ this ->getPidFromFile (true )) {
390+ return Show::error ("The swoole server( {$ this ->name }) is not started. " , true );
391+ }
392+
393+ // SIGUSR1: 向管理进程发送信号,将平稳地重启所有worker进程; 也可在PHP代码中调用`$server->reload()`完成此操作
394+ $ sig = SIGUSR1 ;
395+
396+ // SIGUSR2: only reload task worker
397+ if ($ onlyTaskWorker ) {
398+ $ sig = SIGUSR2 ;
399+ Show::notice ('Will only reload task worker ' );
400+ }
344401
402+ if (!posix_kill ($ masterPid , $ sig )) {
403+ Show::error ("The swoole server( {$ this ->name }) worker process reload fail! " , -1 );
404+ }
405+
406+ return Show::success ("The swoole server( {$ this ->name }) worker process reload success. " , 0 );
345407 }
346408
347- public function stop ()
409+ /**
410+ * Do restart server
411+ * @param null|bool $daemon
412+ */
413+ public function restart ($ daemon = null )
348414 {
349- if ($ this ->ws -> isRunning ( )) {
350- $ this ->cliOut -> error ( ' server is not running! ' );
415+ if ($ this ->getPidFromFile ( true )) {
416+ $ this ->stop ( false );
351417 }
352418
353- ProcessHelper::kill ($ this ->ws ->getPid ());
419+ $ this ->start ($ daemon );
420+ }
421+
422+ /**
423+ * Do stop swoole server
424+ * @param boolean $quit Quit, When stop success?
425+ * @return int
426+ */
427+ public function stop ($ quit = true )
428+ {
429+ if (!$ masterPid = $ this ->getPidFromFile (true )) {
430+ return Show::error ("The swoole server( {$ this ->name }) is not running. " , true );
431+ }
432+
433+ Show::write ("The swoole server( {$ this ->name }: {$ masterPid }) process stopping " , false );
434+
435+ // do stop
436+ // 向主进程发送此信号(SIGTERM)服务器将安全终止;也可在PHP代码中调用`$server->shutdown()` 完成此操作
437+ $ masterPid && posix_kill ($ masterPid , SIGTERM );
438+
439+ $ timeout = 10 ;
440+ $ startTime = time ();
441+
442+ // retry stop if not stopped.
443+ while (true ) {
444+ Show::write ('. ' , false );
445+
446+ if (!@posix_kill ($ masterPid , 0 )) {
447+ break ;
448+ }
449+
450+ // have been timeout
451+ if ((time () - $ startTime ) >= $ timeout ) {
452+ Show::error ("The swoole server( {$ this ->name }) process stop fail! " , -1 );
453+ }
454+
455+ usleep (300000 );
456+ }
457+
458+ $ this ->removePidFile ();
459+
460+ // stop success
461+ return Show::write (" <success>Stopped</success> \nThe swoole server( {$ this ->name }) process stop success " , $ quit );
462+ }
463+
464+ public function help ()
465+ {
466+ $ this ->showHelpInfo ($ this ->cliIn ->getScript ());
467+ }
468+
469+ public function info ()
470+ {
471+ $ this ->showInformation ();
472+ }
473+
474+ public function status ()
475+ {
476+ $ this ->showRuntimeStatus ();
477+ }
478+
479+ /**
480+ * Show server info
481+ */
482+ protected function showInformation ()
483+ {
484+ // $swOpts = $this->config['swoole'];
485+ // $main = $this->config['main_server'];
486+ $ panelData = [
487+ 'System Info ' => [
488+ 'PHP Version ' => PHP_VERSION ,
489+ 'Operate System ' => PHP_OS ,
490+ ],
491+ ];
492+
493+
494+ // 'Server Information'
495+ Show::mList ($ panelData );
496+ // Show::panel($panelData, 'Server Information');
497+ }
498+
499+ /**
500+ * show server runtime status information
501+ */
502+ protected function showRuntimeStatus ()
503+ {
504+ Show::notice ('Sorry, The function un-completed! ' , 0 );
354505 }
355506
356507 /**
357508 * Show help
509+ * @param $scriptName
358510 * @param boolean $showHelpAfterQuit
359511 */
360- public function showHelpInfo ($ showHelpAfterQuit = true )
512+ public function showHelpInfo ($ scriptName , $ showHelpAfterQuit = true )
361513 {
362- $ scriptName = $ this ->cliIn ->getScriptName ();
363-
364514 // 'bin/test_server.php'
365515 if (strpos ($ scriptName , '. ' ) && 'php ' === pathinfo ($ scriptName , PATHINFO_EXTENSION )) {
366516 $ scriptName = 'php ' . $ scriptName ;
367517 }
368518
369- $ this -> cliOut -> helpPanel ([
519+ Show:: helpPanel ([
370520 'description ' => 'webSocket server tool, Version <comment> ' . ServerAbstracter::VERSION .
371521 '</comment> Update time ' . ServerAbstracter::UPDATE_TIME ,
372522 'usage ' => "$ scriptName {start|reload|restart|stop|status} [-d] " ,
@@ -393,6 +543,40 @@ public function showHelpInfo($showHelpAfterQuit = true)
393543 ], $ showHelpAfterQuit );
394544 }
395545
546+ /**
547+ * @param bool $checkRunning
548+ * @return int
549+ */
550+ public function getPidFromFile ($ checkRunning = false )
551+ {
552+ return ProcessHelper::getPidFromFile ($ this ->pidFile , $ checkRunning );
553+ }
554+
555+ /**
556+ * @param (int) $masterPid
557+ * @return bool|int
558+ */
559+ protected function createPidFile ($ masterPid )
560+ {
561+ if ($ this ->pidFile ) {
562+ return file_put_contents ($ this ->pidFile , $ masterPid );
563+ }
564+
565+ return false ;
566+ }
567+
568+ /**
569+ * @return bool
570+ */
571+ protected function removePidFile ()
572+ {
573+ if ($ this ->pidFile && file_exists ($ this ->pidFile )) {
574+ return unlink ($ this ->pidFile );
575+ }
576+
577+ return false ;
578+ }
579+
396580 /**
397581 * webSocket 只会在连接握手时会有 request, response
398582 * @param Request $request
0 commit comments