2015-04-04 21:46:31 +08:00
< ? php
2016-09-20 21:27:41 +08:00
/**
* This file is part of workerman .
*
* Licensed under The MIT License
* For full copyright and license information , please see the MIT - LICENSE . txt
* Redistributions of files must retain the above copyright notice .
*
* @ author walkor < walkor @ workerman . net >
* @ copyright walkor < walkor @ workerman . net >
* @ link http :// www . workerman . net /
* @ license http :// www . opensource . org / licenses / mit - license . php MIT License
*/
2015-04-04 21:46:31 +08:00
namespace Workerman ;
2016-09-20 21:27:41 +08:00
require_once __DIR__ . '/Lib/Constants.php' ;
use Workerman\Events\EventInterface ;
use Workerman\Connection\ConnectionInterface ;
use Workerman\Connection\TcpConnection ;
use Workerman\Connection\UdpConnection ;
use Workerman\Lib\Timer ;
use Exception ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Worker class
* A container for listening ports
2015-04-04 21:46:31 +08:00
*/
class Worker
{
/**
2016-09-20 21:27:41 +08:00
* Version .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
2016-09-20 21:27:41 +08:00
const VERSION = '3.3.4' ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Status starting .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
const STATUS_STARTING = 1 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Status running .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
const STATUS_RUNNING = 2 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Status shutdown .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
const STATUS_SHUTDOWN = 4 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Status reloading .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
const STATUS_RELOADING = 8 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* After sending the restart command to the child process KILL_WORKER_TIMER_TIME seconds ,
* if the process is still living then forced to kill .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
2016-09-20 21:27:41 +08:00
const KILL_WORKER_TIMER_TIME = 2 ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Default backlog . Backlog is the maximum length of the queue of pending connections .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
2016-09-20 21:27:41 +08:00
const DEFAULT_BACKLOG = 1024 ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Max udp package size .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
2016-09-20 21:27:41 +08:00
const MAX_UDP_PACKAGE_SIZE = 65535 ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Worker id .
*
* @ var int
*/
public $id = 0 ;
/**
* Name of the worker processes .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
public $name = 'none' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Number of worker processes .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
public $count = 1 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Unix user of processes , needs appropriate privileges ( usually root ) .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
public $user = '' ;
2016-09-20 21:27:41 +08:00
/**
* Unix group of processes , needs appropriate privileges ( usually root ) .
*
* @ var string
*/
public $group = '' ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* reloadable .
*
2015-04-04 21:46:31 +08:00
* @ var bool
*/
public $reloadable = true ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* reuse port .
*
* @ var bool
*/
public $reusePort = false ;
/**
* Emitted when worker processes start .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onWorkerStart = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when a socket connection is successfully established .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onConnect = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when data is received .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onMessage = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when the other end of the socket sends a FIN packet .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onClose = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when an error occurs with connection .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onError = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when the send buffer becomes full .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onBufferFull = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when the send buffer becomes empty .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onBufferDrain = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Emitted when worker processes stoped .
*
2015-04-04 21:46:31 +08:00
* @ var callback
*/
public $onWorkerStop = null ;
2016-09-20 21:27:41 +08:00
/**
* Emitted when worker processes get reload command .
*
* @ var callback
*/
public $onWorkerReload = null ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Transport layer protocol .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
public $transport = 'tcp' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Store all connections of clients .
*
2015-04-04 21:46:31 +08:00
* @ var array
*/
public $connections = array ();
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Application layer protocol .
*
* @ var Protocols\ProtocolInterface
2015-04-04 21:46:31 +08:00
*/
2016-09-20 21:27:41 +08:00
public $protocol = '' ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Root path for autoload .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
2016-09-20 21:27:41 +08:00
protected $_autoloadRootPath = '' ;
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Daemonize .
*
2015-04-04 21:46:31 +08:00
* @ var bool
*/
public static $daemonize = false ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Stdout file .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
public static $stdoutFile = '/dev/null' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* The file to store master process PID .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
public static $pidFile = '' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Log file .
*
* @ var mixed
2015-04-04 21:46:31 +08:00
*/
public static $logFile = '' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Global event loop .
*
* @ var Events\EventInterface
2015-04-04 21:46:31 +08:00
*/
public static $globalEvent = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* The PID of master process .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
protected static $_masterPid = 0 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Listening socket .
*
* @ var resource
2015-04-04 21:46:31 +08:00
*/
protected $_mainSocket = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Socket name . The format is like this http :// 0.0 . 0.0 : 80 .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
protected $_socketName = '' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Context of socket .
*
* @ var resource
2015-04-04 21:46:31 +08:00
*/
protected $_context = null ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* All worker instances .
*
2015-04-04 21:46:31 +08:00
* @ var array
*/
protected static $_workers = array ();
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* All worker porcesses pid .
* The format is like this [ worker_id => [ pid => pid , pid => pid , .. ], .. ]
*
2015-04-04 21:46:31 +08:00
* @ var array
*/
protected static $_pidMap = array ();
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* All worker processes waiting for restart .
* The format is like this [ pid => pid , pid => pid ] .
*
2015-04-04 21:46:31 +08:00
* @ var array
*/
protected static $_pidsToRestart = array ();
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Mapping from PID to worker process ID .
* The format is like this [ worker_id => [ 0 => $pid , 1 => $pid , .. ], .. ] .
*
* @ var array
*/
protected static $_idMap = array ();
/**
* Current status .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
protected static $_status = self :: STATUS_STARTING ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Maximum length of the worker names .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
protected static $_maxWorkerNameLength = 12 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Maximum length of the socket names .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
protected static $_maxSocketNameLength = 12 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Maximum length of the process user names .
*
2015-04-04 21:46:31 +08:00
* @ var int
*/
protected static $_maxUserNameLength = 12 ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* The file to store status info of current worker process .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
protected static $_statisticsFile = '' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Start file .
*
2015-04-04 21:46:31 +08:00
* @ var string
*/
protected static $_startFile = '' ;
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Status info of current worker process .
*
2015-04-04 21:46:31 +08:00
* @ var array
*/
protected static $_globalStatistics = array (
2016-09-20 21:27:41 +08:00
'start_timestamp' => 0 ,
2015-04-04 21:46:31 +08:00
'worker_exit_info' => array ()
);
2016-09-20 21:27:41 +08:00
/**
* Available event loops .
*
* @ var array
*/
protected static $_availableEventLoops = array (
'libevent' ,
'event' ,
'ev'
);
/**
* Current eventLoop name .
*
* @ var string
*/
protected static $_eventLoopName = 'select' ;
/**
* PHP built - in protocols .
*
* @ var array
*/
protected static $_builtinTransports = array (
'tcp' => 'tcp' ,
'udp' => 'udp' ,
'unix' => 'unix' ,
'ssl' => 'tcp' ,
'sslv2' => 'tcp' ,
'sslv3' => 'tcp' ,
'tls' => 'tcp'
);
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Run all worker instances .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
public static function runAll ()
{
2016-09-20 21:27:41 +08:00
self :: checkSapiEnv ();
2015-04-04 21:46:31 +08:00
self :: init ();
self :: parseCommand ();
self :: daemonize ();
self :: initWorkers ();
self :: installSignal ();
self :: saveMasterPid ();
self :: forkWorkers ();
2016-09-20 21:27:41 +08:00
self :: displayUI ();
self :: resetStd ();
2015-04-04 21:46:31 +08:00
self :: monitorWorkers ();
}
2016-09-20 21:27:41 +08:00
/**
* Check sapi .
*
* @ return void
*/
protected static function checkSapiEnv ()
{
// Only for cli.
if ( php_sapi_name () != " cli " ) {
exit ( " only run in command line mode \n " );
}
}
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Init .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
2016-09-20 21:27:41 +08:00
protected static function init ()
2015-04-04 21:46:31 +08:00
{
2016-09-20 21:27:41 +08:00
// Start file.
$backtrace = debug_backtrace ();
self :: $_startFile = $backtrace [ count ( $backtrace ) - 1 ][ 'file' ];
// Pid file.
if ( empty ( self :: $pidFile )) {
self :: $pidFile = __DIR__ . " /../ " . str_replace ( '/' , '_' , self :: $_startFile ) . " .pid " ;
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Log file.
if ( empty ( self :: $logFile )) {
2015-04-04 21:46:31 +08:00
self :: $logFile = __DIR__ . '/../workerman.log' ;
}
2016-09-20 21:27:41 +08:00
touch ( self :: $logFile );
chmod ( self :: $logFile , 0622 );
// State.
2015-04-04 21:46:31 +08:00
self :: $_status = self :: STATUS_STARTING ;
2016-09-20 21:27:41 +08:00
// For statistics.
2015-04-04 21:46:31 +08:00
self :: $_globalStatistics [ 'start_timestamp' ] = time ();
2016-09-20 21:27:41 +08:00
self :: $_statisticsFile = sys_get_temp_dir () . '/workerman.status' ;
// Process title.
2015-04-04 21:46:31 +08:00
self :: setProcessTitle ( 'WorkerMan: master process start_file=' . self :: $_startFile );
2016-09-20 21:27:41 +08:00
// Init data for worker id.
self :: initId ();
// Timer init.
2015-04-04 21:46:31 +08:00
Timer :: init ();
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Init All worker instances .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function initWorkers ()
{
2016-09-20 21:27:41 +08:00
foreach ( self :: $_workers as $worker ) {
// Worker name.
if ( empty ( $worker -> name )) {
2015-04-04 21:46:31 +08:00
$worker -> name = 'none' ;
}
2016-09-20 21:27:41 +08:00
// Get maximum length of worker name.
2015-04-04 21:46:31 +08:00
$worker_name_length = strlen ( $worker -> name );
2016-09-20 21:27:41 +08:00
if ( self :: $_maxWorkerNameLength < $worker_name_length ) {
2015-04-04 21:46:31 +08:00
self :: $_maxWorkerNameLength = $worker_name_length ;
}
2016-09-20 21:27:41 +08:00
// Get maximum length of socket name.
2015-04-04 21:46:31 +08:00
$socket_name_length = strlen ( $worker -> getSocketName ());
2016-09-20 21:27:41 +08:00
if ( self :: $_maxSocketNameLength < $socket_name_length ) {
2015-04-04 21:46:31 +08:00
self :: $_maxSocketNameLength = $socket_name_length ;
}
2016-09-20 21:27:41 +08:00
// Get unix user of the worker process.
if ( empty ( $worker -> user )) {
2015-04-04 21:46:31 +08:00
$worker -> user = self :: getCurrentUser ();
2016-09-20 21:27:41 +08:00
} else {
if ( posix_getuid () !== 0 && $worker -> user != self :: getCurrentUser ()) {
self :: log ( 'Warning: You must have the root privileges to change uid and gid.' );
}
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Get maximum length of unix user name.
2015-04-04 21:46:31 +08:00
$user_name_length = strlen ( $worker -> user );
2016-09-20 21:27:41 +08:00
if ( self :: $_maxUserNameLength < $user_name_length ) {
2015-04-04 21:46:31 +08:00
self :: $_maxUserNameLength = $user_name_length ;
}
2016-09-20 21:27:41 +08:00
// Listen.
if ( ! $worker -> reusePort ) {
$worker -> listen ();
}
2015-04-04 21:46:31 +08:00
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Init idMap .
* return void
*/
protected static function initId ()
{
foreach ( self :: $_workers as $worker_id => $worker ) {
self :: $_idMap [ $worker_id ] = array_fill ( 0 , $worker -> count , 0 );
}
}
/**
* Get unix user of current porcess .
*
2015-04-04 21:46:31 +08:00
* @ return string
*/
protected static function getCurrentUser ()
{
$user_info = posix_getpwuid ( posix_getuid ());
return $user_info [ 'name' ];
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Display staring UI .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function displayUI ()
{
echo " \033 [1A \n \033 [K----------------------- \033 [47;30m WORKERMAN \033 [0m----------------------------- \n \033 [0m " ;
2016-09-20 21:27:41 +08:00
echo 'Workerman version:' , Worker :: VERSION , " PHP version: " , PHP_VERSION , " \n " ;
2015-04-04 21:46:31 +08:00
echo " ------------------------ \033 [47;30m WORKERS \033 [0m------------------------------- \n " ;
2016-09-20 21:27:41 +08:00
echo " \033 [47;30muser \033 [0m " , str_pad ( '' ,
self :: $_maxUserNameLength + 2 - strlen ( 'user' )), " \033 [47;30mworker \033 [0m " , str_pad ( '' ,
self :: $_maxWorkerNameLength + 2 - strlen ( 'worker' )), " \033 [47;30mlisten \033 [0m " , str_pad ( '' ,
self :: $_maxSocketNameLength + 2 - strlen ( 'listen' )), " \033 [47;30mprocesses \033 [0m \033 [47;30m " , " status \033 [0m \n " ;
foreach ( self :: $_workers as $worker ) {
echo str_pad ( $worker -> user , self :: $_maxUserNameLength + 2 ), str_pad ( $worker -> name ,
self :: $_maxWorkerNameLength + 2 ), str_pad ( $worker -> getSocketName (),
self :: $_maxSocketNameLength + 2 ), str_pad ( ' ' . $worker -> count , 9 ), " \033 [32;40m [OK] \033 [0m \n " ;;
2015-04-04 21:46:31 +08:00
}
echo " ---------------------------------------------------------------- \n " ;
2016-09-20 21:27:41 +08:00
if ( self :: $daemonize ) {
global $argv ;
$start_file = $argv [ 0 ];
echo " Input \" php $start_file stop \" to quit. Start success. \n " ;
} else {
echo " Press Ctrl-C to quit. Start success. \n " ;
}
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Parse command .
2015-04-04 21:46:31 +08:00
* php yourfile . php start | stop | restart | reload | status
2016-09-20 21:27:41 +08:00
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
2016-09-20 21:27:41 +08:00
protected static function parseCommand ()
2015-04-04 21:46:31 +08:00
{
global $argv ;
2016-09-20 21:27:41 +08:00
// Check argv;
$start_file = $argv [ 0 ];
if ( ! isset ( $argv [ 1 ])) {
exit ( " Usage: php yourfile.php { start|stop|restart|reload|status|kill} \n " );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Get command.
$command = trim ( $argv [ 1 ]);
2015-04-04 21:46:31 +08:00
$command2 = isset ( $argv [ 2 ]) ? $argv [ 2 ] : '' ;
2016-09-20 21:27:41 +08:00
// Start command.
$mode = '' ;
if ( $command === 'start' ) {
if ( $command2 === '-d' || Worker :: $daemonize ) {
$mode = 'in DAEMON mode' ;
} else {
$mode = 'in DEBUG mode' ;
2015-04-04 21:46:31 +08:00
}
}
2016-09-20 21:27:41 +08:00
self :: log ( " Workerman[ $start_file ] $command $mode " );
// Get master process PID.
$master_pid = @ file_get_contents ( self :: $pidFile );
$master_is_alive = $master_pid && @ posix_kill ( $master_pid , 0 );
// Master is still alive?
if ( $master_is_alive ) {
if ( $command === 'start' && posix_getpid () != $master_pid ) {
self :: log ( " Workerman[ $start_file ] already running " );
exit ;
}
} elseif ( $command !== 'start' && $command !== 'restart' && $command !== 'kill' ) {
2015-04-04 21:46:31 +08:00
self :: log ( " Workerman[ $start_file ] not run " );
2016-09-20 21:27:41 +08:00
exit ;
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// execute command.
switch ( $command ) {
case 'kill' :
exec ( " ps aux | grep $start_file | grep -v grep | awk ' { print $ 2}' |xargs kill -SIGINT " );
usleep ( 100000 );
exec ( " ps aux | grep $start_file | grep -v grep | awk ' { print $ 2}' |xargs kill -SIGKILL " );
break ;
2015-04-04 21:46:31 +08:00
case 'start' :
2016-09-20 21:27:41 +08:00
if ( $command2 === '-d' ) {
2015-04-04 21:46:31 +08:00
Worker :: $daemonize = true ;
}
break ;
case 'status' :
2016-09-20 21:27:41 +08:00
if ( is_file ( self :: $_statisticsFile )) {
2015-04-04 21:46:31 +08:00
@ unlink ( self :: $_statisticsFile );
}
2016-09-20 21:27:41 +08:00
// Master process will send status signal to all child processes.
2015-04-04 21:46:31 +08:00
posix_kill ( $master_pid , SIGUSR2 );
2016-09-20 21:27:41 +08:00
// Waiting amoment.
2015-04-04 21:46:31 +08:00
usleep ( 100000 );
2016-09-20 21:27:41 +08:00
// Display statisitcs data from a disk file.
@ readfile ( self :: $_statisticsFile );
2015-04-04 21:46:31 +08:00
exit ( 0 );
case 'restart' :
case 'stop' :
self :: log ( " Workerman[ $start_file ] is stoping ... " );
2016-09-20 21:27:41 +08:00
// Send stop signal to master process.
2015-04-04 21:46:31 +08:00
$master_pid && posix_kill ( $master_pid , SIGINT );
2016-09-20 21:27:41 +08:00
// Timeout.
$timeout = 5 ;
2015-04-04 21:46:31 +08:00
$start_time = time ();
2016-09-20 21:27:41 +08:00
// Check master process is still alive?
while ( 1 ) {
2015-04-04 21:46:31 +08:00
$master_is_alive = $master_pid && posix_kill ( $master_pid , 0 );
2016-09-20 21:27:41 +08:00
if ( $master_is_alive ) {
// Timeout?
if ( time () - $start_time >= $timeout ) {
2015-04-04 21:46:31 +08:00
self :: log ( " Workerman[ $start_file ] stop fail " );
exit ;
}
2016-09-20 21:27:41 +08:00
// Waiting amoment.
2015-04-04 21:46:31 +08:00
usleep ( 10000 );
continue ;
}
2016-09-20 21:27:41 +08:00
// Stop success.
2015-04-04 21:46:31 +08:00
self :: log ( " Workerman[ $start_file ] stop success " );
2016-09-20 21:27:41 +08:00
if ( $command === 'stop' ) {
2015-04-04 21:46:31 +08:00
exit ( 0 );
}
2016-09-20 21:27:41 +08:00
if ( $command2 === '-d' ) {
2015-04-04 21:46:31 +08:00
Worker :: $daemonize = true ;
}
break ;
}
break ;
case 'reload' :
posix_kill ( $master_pid , SIGUSR1 );
self :: log ( " Workerman[ $start_file ] reload " );
exit ;
default :
2016-09-20 21:27:41 +08:00
exit ( " Usage: php yourfile.php { start|stop|restart|reload|status|kill} \n " );
2015-04-04 21:46:31 +08:00
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Install signal handler .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function installSignal ()
{
// stop
2016-09-20 21:27:41 +08:00
pcntl_signal ( SIGINT , array ( '\Workerman\Worker' , 'signalHandler' ), false );
2015-04-04 21:46:31 +08:00
// reload
pcntl_signal ( SIGUSR1 , array ( '\Workerman\Worker' , 'signalHandler' ), false );
// status
pcntl_signal ( SIGUSR2 , array ( '\Workerman\Worker' , 'signalHandler' ), false );
// ignore
pcntl_signal ( SIGPIPE , SIG_IGN , false );
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Reinstall signal handler .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function reinstallSignal ()
{
// uninstall stop signal handler
2016-09-20 21:27:41 +08:00
pcntl_signal ( SIGINT , SIG_IGN , false );
2015-04-04 21:46:31 +08:00
// uninstall reload signal handler
pcntl_signal ( SIGUSR1 , SIG_IGN , false );
// uninstall status signal handler
pcntl_signal ( SIGUSR2 , SIG_IGN , false );
// reinstall stop signal handler
self :: $globalEvent -> add ( SIGINT , EventInterface :: EV_SIGNAL , array ( '\Workerman\Worker' , 'signalHandler' ));
2016-09-20 21:27:41 +08:00
// reinstall reload signal handler
self :: $globalEvent -> add ( SIGUSR1 , EventInterface :: EV_SIGNAL , array ( '\Workerman\Worker' , 'signalHandler' ));
// reinstall status signal handler
2015-04-04 21:46:31 +08:00
self :: $globalEvent -> add ( SIGUSR2 , EventInterface :: EV_SIGNAL , array ( '\Workerman\Worker' , 'signalHandler' ));
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Signal handler .
*
2015-04-04 21:46:31 +08:00
* @ param int $signal
*/
public static function signalHandler ( $signal )
{
2016-09-20 21:27:41 +08:00
switch ( $signal ) {
// Stop.
2015-04-04 21:46:31 +08:00
case SIGINT :
self :: stopAll ();
break ;
2016-09-20 21:27:41 +08:00
// Reload.
2015-04-04 21:46:31 +08:00
case SIGUSR1 :
2016-09-20 21:27:41 +08:00
self :: $_pidsToRestart = self :: getAllWorkerPids ();
2015-04-04 21:46:31 +08:00
self :: reload ();
break ;
2016-09-20 21:27:41 +08:00
// Show status.
2015-04-04 21:46:31 +08:00
case SIGUSR2 :
self :: writeStatisticsToStatusFile ();
break ;
}
}
/**
2016-09-20 21:27:41 +08:00
* Run as deamon mode .
*
2015-04-04 21:46:31 +08:00
* @ throws Exception
*/
protected static function daemonize ()
{
2016-09-20 21:27:41 +08:00
if ( ! self :: $daemonize ) {
2015-04-04 21:46:31 +08:00
return ;
}
umask ( 0 );
$pid = pcntl_fork ();
2016-09-20 21:27:41 +08:00
if ( - 1 === $pid ) {
2015-04-04 21:46:31 +08:00
throw new Exception ( 'fork fail' );
2016-09-20 21:27:41 +08:00
} elseif ( $pid > 0 ) {
2015-04-04 21:46:31 +08:00
exit ( 0 );
}
2016-09-20 21:27:41 +08:00
if ( - 1 === posix_setsid ()) {
2015-04-04 21:46:31 +08:00
throw new Exception ( " setsid fail " );
}
2016-09-20 21:27:41 +08:00
// Fork again avoid SVR4 system regain the control of terminal.
2015-04-04 21:46:31 +08:00
$pid = pcntl_fork ();
2016-09-20 21:27:41 +08:00
if ( - 1 === $pid ) {
2015-04-04 21:46:31 +08:00
throw new Exception ( " fork fail " );
2016-09-20 21:27:41 +08:00
} elseif ( 0 !== $pid ) {
2015-04-04 21:46:31 +08:00
exit ( 0 );
}
}
/**
2016-09-20 21:27:41 +08:00
* Redirect standard input and output .
*
2015-04-04 21:46:31 +08:00
* @ throws Exception
*/
protected static function resetStd ()
{
2016-09-20 21:27:41 +08:00
if ( ! self :: $daemonize ) {
2015-04-04 21:46:31 +08:00
return ;
}
global $STDOUT , $STDERR ;
2016-09-20 21:27:41 +08:00
$handle = fopen ( self :: $stdoutFile , " a " );
if ( $handle ) {
2015-04-04 21:46:31 +08:00
unset ( $handle );
@ fclose ( STDOUT );
@ fclose ( STDERR );
2016-09-20 21:27:41 +08:00
$STDOUT = fopen ( self :: $stdoutFile , " a " );
$STDERR = fopen ( self :: $stdoutFile , " a " );
} else {
2015-04-04 21:46:31 +08:00
throw new Exception ( 'can not open stdoutFile ' . self :: $stdoutFile );
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Save pid .
*
2015-04-04 21:46:31 +08:00
* @ throws Exception
*/
protected static function saveMasterPid ()
{
self :: $_masterPid = posix_getpid ();
2016-09-20 21:27:41 +08:00
if ( false === @ file_put_contents ( self :: $pidFile , self :: $_masterPid )) {
2015-04-04 21:46:31 +08:00
throw new Exception ( 'can not save pid to ' . self :: $pidFile );
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Get event loop name .
*
* @ return string
*/
protected static function getEventLoopName ()
{
foreach ( self :: $_availableEventLoops as $name ) {
if ( extension_loaded ( $name )) {
self :: $_eventLoopName = $name ;
break ;
}
}
return self :: $_eventLoopName ;
}
/**
* Get all pids of worker processes .
*
2015-04-04 21:46:31 +08:00
* @ return array
*/
protected static function getAllWorkerPids ()
{
2016-09-20 21:27:41 +08:00
$pid_array = array ();
foreach ( self :: $_pidMap as $worker_pid_array ) {
foreach ( $worker_pid_array as $worker_pid ) {
2015-04-04 21:46:31 +08:00
$pid_array [ $worker_pid ] = $worker_pid ;
}
}
return $pid_array ;
}
/**
2016-09-20 21:27:41 +08:00
* Fork some worker processes .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function forkWorkers ()
{
2016-09-20 21:27:41 +08:00
foreach ( self :: $_workers as $worker ) {
if ( self :: $_status === self :: STATUS_STARTING ) {
if ( empty ( $worker -> name )) {
2015-04-04 21:46:31 +08:00
$worker -> name = $worker -> getSocketName ();
}
$worker_name_length = strlen ( $worker -> name );
2016-09-20 21:27:41 +08:00
if ( self :: $_maxWorkerNameLength < $worker_name_length ) {
2015-04-04 21:46:31 +08:00
self :: $_maxWorkerNameLength = $worker_name_length ;
}
}
2016-09-20 21:27:41 +08:00
while ( count ( self :: $_pidMap [ $worker -> workerId ]) < $worker -> count ) {
static :: forkOneWorker ( $worker );
2015-04-04 21:46:31 +08:00
}
}
}
/**
2016-09-20 21:27:41 +08:00
* Fork one worker process .
*
2015-04-04 21:46:31 +08:00
* @ param Worker $worker
* @ throws Exception
*/
protected static function forkOneWorker ( $worker )
{
$pid = pcntl_fork ();
2016-09-20 21:27:41 +08:00
// Get available worker id.
$id = self :: getId ( $worker -> workerId , 0 );
// For master process.
if ( $pid > 0 ) {
2015-04-04 21:46:31 +08:00
self :: $_pidMap [ $worker -> workerId ][ $pid ] = $pid ;
2016-09-20 21:27:41 +08:00
self :: $_idMap [ $worker -> workerId ][ $id ] = $pid ;
} // For child processes.
elseif ( 0 === $pid ) {
if ( $worker -> reusePort ) {
$worker -> listen ();
}
if ( self :: $_status === self :: STATUS_STARTING ) {
self :: resetStd ();
}
self :: $_pidMap = array ();
2015-04-04 21:46:31 +08:00
self :: $_workers = array ( $worker -> workerId => $worker );
Timer :: delAll ();
self :: setProcessTitle ( 'WorkerMan: worker process ' . $worker -> name . ' ' . $worker -> getSocketName ());
2016-09-20 21:27:41 +08:00
$worker -> setUserAndGroup ();
$worker -> id = $id ;
2015-04-04 21:46:31 +08:00
$worker -> run ();
exit ( 250 );
2016-09-20 21:27:41 +08:00
} else {
2015-04-04 21:46:31 +08:00
throw new Exception ( " forkOneWorker fail " );
}
}
2016-09-20 21:27:41 +08:00
/**
* Get worker id .
*
* @ param int $worker_id
* @ param int $pid
*/
protected static function getId ( $worker_id , $pid )
{
$id = array_search ( $pid , self :: $_idMap [ $worker_id ]);
if ( $id === false ) {
echo " getId fail \n " ;
}
return $id ;
}
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Set unix user and group for current process .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
2016-09-20 21:27:41 +08:00
public function setUserAndGroup ()
2015-04-04 21:46:31 +08:00
{
2016-09-20 21:27:41 +08:00
// Get uid.
$user_info = posix_getpwnam ( $this -> user );
if ( ! $user_info ) {
self :: log ( " Warning: User { $this -> user } not exsits " );
2015-04-04 21:46:31 +08:00
return ;
}
2016-09-20 21:27:41 +08:00
$uid = $user_info [ 'uid' ];
// Get gid.
if ( $this -> group ) {
$group_info = posix_getgrnam ( $this -> group );
if ( ! $group_info ) {
self :: log ( " Warning: Group { $this -> group } not exsits " );
return ;
}
$gid = $group_info [ 'gid' ];
} else {
$gid = $user_info [ 'gid' ];
}
// Set uid and gid.
if ( $uid != posix_getuid () || $gid != posix_getgid ()) {
if ( ! posix_setgid ( $gid ) || ! posix_initgroups ( $user_info [ 'name' ], $gid ) || ! posix_setuid ( $uid )) {
self :: log ( " Warning: change gid or uid fail. " );
2015-04-04 21:46:31 +08:00
}
}
}
/**
2016-09-20 21:27:41 +08:00
* Set process name .
*
2015-04-04 21:46:31 +08:00
* @ param string $title
* @ return void
*/
protected static function setProcessTitle ( $title )
{
// >=php 5.5
2016-09-20 21:27:41 +08:00
if ( function_exists ( 'cli_set_process_title' )) {
2015-04-04 21:46:31 +08:00
@ cli_set_process_title ( $title );
2016-09-20 21:27:41 +08:00
} // Need proctitle when php<=5.5 .
elseif ( extension_loaded ( 'proctitle' ) && function_exists ( 'setproctitle' )) {
2015-04-04 21:46:31 +08:00
@ setproctitle ( $title );
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Monitor all child processes .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function monitorWorkers ()
{
self :: $_status = self :: STATUS_RUNNING ;
2016-09-20 21:27:41 +08:00
while ( 1 ) {
// Calls signal handlers for pending signals.
2015-04-04 21:46:31 +08:00
pcntl_signal_dispatch ();
2016-09-20 21:27:41 +08:00
// Suspends execution of the current process until a child has exited, or until a signal is delivered
2015-04-04 21:46:31 +08:00
$status = 0 ;
2016-09-20 21:27:41 +08:00
$pid = pcntl_wait ( $status , WUNTRACED );
// Calls signal handlers for pending signals again.
pcntl_signal_dispatch ();
// If a child has already exited.
if ( $pid > 0 ) {
// Find out witch worker process exited.
foreach ( self :: $_pidMap as $worker_id => $worker_pid_array ) {
if ( isset ( $worker_pid_array [ $pid ])) {
2015-04-04 21:46:31 +08:00
$worker = self :: $_workers [ $worker_id ];
2016-09-20 21:27:41 +08:00
// Exit status.
if ( $status !== 0 ) {
self :: log ( " worker[ " . $worker -> name . " : $pid ] exit with status $status " );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// For Statistics.
if ( ! isset ( self :: $_globalStatistics [ 'worker_exit_info' ][ $worker_id ][ $status ])) {
2015-04-04 21:46:31 +08:00
self :: $_globalStatistics [ 'worker_exit_info' ][ $worker_id ][ $status ] = 0 ;
}
self :: $_globalStatistics [ 'worker_exit_info' ][ $worker_id ][ $status ] ++ ;
2016-09-20 21:27:41 +08:00
// Clear process data.
2015-04-04 21:46:31 +08:00
unset ( self :: $_pidMap [ $worker_id ][ $pid ]);
2016-09-20 21:27:41 +08:00
// Mark id is available.
$id = self :: getId ( $worker_id , $pid );
self :: $_idMap [ $worker_id ][ $id ] = 0 ;
2015-04-04 21:46:31 +08:00
break ;
}
}
2016-09-20 21:27:41 +08:00
// Is still running state then fork a new worker process.
if ( self :: $_status !== self :: STATUS_SHUTDOWN ) {
2015-04-04 21:46:31 +08:00
self :: forkWorkers ();
2016-09-20 21:27:41 +08:00
// If reloading continue.
if ( isset ( self :: $_pidsToRestart [ $pid ])) {
2015-04-04 21:46:31 +08:00
unset ( self :: $_pidsToRestart [ $pid ]);
self :: reload ();
}
2016-09-20 21:27:41 +08:00
} else {
// If shutdown state and all child processes exited then master process exit.
if ( ! self :: getAllWorkerPids ()) {
2015-04-04 21:46:31 +08:00
self :: exitAndClearAll ();
}
}
2016-09-20 21:27:41 +08:00
} else {
// If shutdown state and all child processes exited then master process exit.
if ( self :: $_status === self :: STATUS_SHUTDOWN && ! self :: getAllWorkerPids ()) {
self :: exitAndClearAll ();
2015-04-04 21:46:31 +08:00
}
}
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Exit current process .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function exitAndClearAll ()
{
2016-09-20 21:27:41 +08:00
foreach ( self :: $_workers as $worker ) {
$socket_name = $worker -> getSocketName ();
if ( $worker -> transport === 'unix' && $socket_name ) {
list (, $address ) = explode ( ':' , $socket_name , 2 );
@ unlink ( $address );
}
}
2015-04-04 21:46:31 +08:00
@ unlink ( self :: $pidFile );
2016-09-20 21:27:41 +08:00
self :: log ( " Workerman[ " . basename ( self :: $_startFile ) . " ] has been stopped " );
2015-04-04 21:46:31 +08:00
exit ( 0 );
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Execute reload .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function reload ()
{
2016-09-20 21:27:41 +08:00
// For master process.
if ( self :: $_masterPid === posix_getpid ()) {
// Set reloading state.
if ( self :: $_status !== self :: STATUS_RELOADING && self :: $_status !== self :: STATUS_SHUTDOWN ) {
self :: log ( " Workerman[ " . basename ( self :: $_startFile ) . " ] reloading " );
2015-04-04 21:46:31 +08:00
self :: $_status = self :: STATUS_RELOADING ;
}
2016-09-20 21:27:41 +08:00
// Send reload signal to all child processes.
2015-04-04 21:46:31 +08:00
$reloadable_pid_array = array ();
2016-09-20 21:27:41 +08:00
foreach ( self :: $_pidMap as $worker_id => $worker_pid_array ) {
2015-04-04 21:46:31 +08:00
$worker = self :: $_workers [ $worker_id ];
2016-09-20 21:27:41 +08:00
if ( $worker -> reloadable ) {
foreach ( $worker_pid_array as $pid ) {
2015-04-04 21:46:31 +08:00
$reloadable_pid_array [ $pid ] = $pid ;
}
2016-09-20 21:27:41 +08:00
} else {
foreach ( $worker_pid_array as $pid ) {
// Send reload signal to a worker process which reloadable is false.
posix_kill ( $pid , SIGUSR1 );
}
2015-04-04 21:46:31 +08:00
}
}
2016-09-20 21:27:41 +08:00
// Get all pids that are waiting reload.
self :: $_pidsToRestart = array_intersect ( self :: $_pidsToRestart , $reloadable_pid_array );
// Reload complete.
if ( empty ( self :: $_pidsToRestart )) {
if ( self :: $_status !== self :: STATUS_SHUTDOWN ) {
2015-04-04 21:46:31 +08:00
self :: $_status = self :: STATUS_RUNNING ;
}
return ;
}
2016-09-20 21:27:41 +08:00
// Continue reload.
$one_worker_pid = current ( self :: $_pidsToRestart );
// Send reload signal to a worker process.
2015-04-04 21:46:31 +08:00
posix_kill ( $one_worker_pid , SIGUSR1 );
2016-09-20 21:27:41 +08:00
// If the process does not exit after self::KILL_WORKER_TIMER_TIME seconds try to kill it.
2015-04-04 21:46:31 +08:00
Timer :: add ( self :: KILL_WORKER_TIMER_TIME , 'posix_kill' , array ( $one_worker_pid , SIGKILL ), false );
2016-09-20 21:27:41 +08:00
} // For child processes.
else {
2015-04-04 21:46:31 +08:00
$worker = current ( self :: $_workers );
2016-09-20 21:27:41 +08:00
// Try to emit onWorkerReload callback.
if ( $worker -> onWorkerReload ) {
try {
call_user_func ( $worker -> onWorkerReload , $worker );
} catch ( \Exception $e ) {
self :: log ( $e );
exit ( 250 );
} catch ( \Error $e ) {
self :: log ( $e );
exit ( 250 );
}
}
if ( $worker -> reloadable ) {
2015-04-04 21:46:31 +08:00
self :: stopAll ();
}
}
2016-09-20 21:27:41 +08:00
}
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Stop .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
public static function stopAll ()
{
self :: $_status = self :: STATUS_SHUTDOWN ;
2016-09-20 21:27:41 +08:00
// For master process.
if ( self :: $_masterPid === posix_getpid ()) {
self :: log ( " Workerman[ " . basename ( self :: $_startFile ) . " ] Stopping ... " );
2015-04-04 21:46:31 +08:00
$worker_pid_array = self :: getAllWorkerPids ();
2016-09-20 21:27:41 +08:00
// Send stop signal to all child processes.
foreach ( $worker_pid_array as $worker_pid ) {
2015-04-04 21:46:31 +08:00
posix_kill ( $worker_pid , SIGINT );
2016-09-20 21:27:41 +08:00
Timer :: add ( self :: KILL_WORKER_TIMER_TIME , 'posix_kill' , array ( $worker_pid , SIGKILL ), false );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
} // For child processes.
else {
// Execute exit.
foreach ( self :: $_workers as $worker ) {
2015-04-04 21:46:31 +08:00
$worker -> stop ();
}
exit ( 0 );
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Write statistics data to disk .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
protected static function writeStatisticsToStatusFile ()
{
2016-09-20 21:27:41 +08:00
// For master process.
if ( self :: $_masterPid === posix_getpid ()) {
2015-04-04 21:46:31 +08:00
$loadavg = sys_getloadavg ();
2016-09-20 21:27:41 +08:00
file_put_contents ( self :: $_statisticsFile ,
" ---------------------------------------GLOBAL STATUS-------------------------------------------- \n " );
file_put_contents ( self :: $_statisticsFile ,
'Workerman version:' . Worker :: VERSION . " PHP version: " . PHP_VERSION . " \n " , FILE_APPEND );
file_put_contents ( self :: $_statisticsFile , 'start time:' . date ( 'Y-m-d H:i:s' ,
self :: $_globalStatistics [ 'start_timestamp' ]) . ' run ' . floor (( time () - self :: $_globalStatistics [ 'start_timestamp' ]) / ( 24 * 60 * 60 )) . ' days ' . floor ((( time () - self :: $_globalStatistics [ 'start_timestamp' ]) % ( 24 * 60 * 60 )) / ( 60 * 60 )) . " hours \n " ,
FILE_APPEND );
$load_str = 'load average: ' . implode ( " , " , $loadavg );
file_put_contents ( self :: $_statisticsFile ,
str_pad ( $load_str , 33 ) . 'event-loop:' . self :: getEventLoopName () . " \n " , FILE_APPEND );
file_put_contents ( self :: $_statisticsFile ,
count ( self :: $_pidMap ) . ' workers ' . count ( self :: getAllWorkerPids ()) . " processes \n " ,
FILE_APPEND );
file_put_contents ( self :: $_statisticsFile ,
str_pad ( 'worker_name' , self :: $_maxWorkerNameLength ) . " exit_status exit_count \n " , FILE_APPEND );
foreach ( self :: $_pidMap as $worker_id => $worker_pid_array ) {
2015-04-04 21:46:31 +08:00
$worker = self :: $_workers [ $worker_id ];
2016-09-20 21:27:41 +08:00
if ( isset ( self :: $_globalStatistics [ 'worker_exit_info' ][ $worker_id ])) {
foreach ( self :: $_globalStatistics [ 'worker_exit_info' ][ $worker_id ] as $worker_exit_status => $worker_exit_count ) {
file_put_contents ( self :: $_statisticsFile ,
str_pad ( $worker -> name , self :: $_maxWorkerNameLength ) . " " . str_pad ( $worker_exit_status ,
16 ) . " $worker_exit_count\n " , FILE_APPEND );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
} else {
file_put_contents ( self :: $_statisticsFile ,
str_pad ( $worker -> name , self :: $_maxWorkerNameLength ) . " " . str_pad ( 0 , 16 ) . " 0 \n " ,
FILE_APPEND );
2015-04-04 21:46:31 +08:00
}
}
2016-09-20 21:27:41 +08:00
file_put_contents ( self :: $_statisticsFile ,
" ---------------------------------------PROCESS STATUS------------------------------------------- \n " ,
FILE_APPEND );
file_put_contents ( self :: $_statisticsFile ,
" pid \t memory " . str_pad ( 'listening' , self :: $_maxSocketNameLength ) . " " . str_pad ( 'worker_name' ,
self :: $_maxWorkerNameLength ) . " connections " . str_pad ( 'total_request' ,
13 ) . " " . str_pad ( 'send_fail' , 9 ) . " " . str_pad ( 'throw_exception' , 15 ) . " \n " , FILE_APPEND );
2015-04-04 21:46:31 +08:00
chmod ( self :: $_statisticsFile , 0722 );
2016-09-20 21:27:41 +08:00
foreach ( self :: getAllWorkerPids () as $worker_pid ) {
2015-04-04 21:46:31 +08:00
posix_kill ( $worker_pid , SIGUSR2 );
}
return ;
}
2016-09-20 21:27:41 +08:00
// For child processes.
/** @var Worker $worker */
$worker = current ( self :: $_workers );
$worker_status_str = posix_getpid () . " \t " . str_pad ( round ( memory_get_usage ( true ) / ( 1024 * 1024 ), 2 ) . " M " ,
7 ) . " " . str_pad ( $worker -> getSocketName (),
self :: $_maxSocketNameLength ) . " " . str_pad (( $worker -> name === $worker -> getSocketName () ? 'none' : $worker -> name ),
self :: $_maxWorkerNameLength ) . " " ;
$worker_status_str .= str_pad ( ConnectionInterface :: $statistics [ 'connection_count' ],
11 ) . " " . str_pad ( ConnectionInterface :: $statistics [ 'total_request' ],
14 ) . " " . str_pad ( ConnectionInterface :: $statistics [ 'send_fail' ],
9 ) . " " . str_pad ( ConnectionInterface :: $statistics [ 'throw_exception' ], 15 ) . " \n " ;
file_put_contents ( self :: $_statisticsFile , $worker_status_str , FILE_APPEND );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Check errors when current process exited .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
public static function checkErrors ()
{
2016-09-20 21:27:41 +08:00
if ( self :: STATUS_SHUTDOWN != self :: $_status ) {
2015-04-04 21:46:31 +08:00
$error_msg = " WORKER EXIT UNEXPECTED " ;
2016-09-20 21:27:41 +08:00
$errors = error_get_last ();
if ( $errors && ( $errors [ 'type' ] === E_ERROR ||
$errors [ 'type' ] === E_PARSE ||
$errors [ 'type' ] === E_CORE_ERROR ||
$errors [ 'type' ] === E_COMPILE_ERROR ||
$errors [ 'type' ] === E_RECOVERABLE_ERROR )
) {
2015-04-04 21:46:31 +08:00
$error_msg .= self :: getErrorType ( $errors [ 'type' ]) . " { $errors [ 'message' ] } in { $errors [ 'file' ] } on line { $errors [ 'line' ] } " ;
}
self :: log ( $error_msg );
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Get error message by error code .
*
2015-04-04 21:46:31 +08:00
* @ param integer $type
* @ return string
*/
protected static function getErrorType ( $type )
{
2016-09-20 21:27:41 +08:00
switch ( $type ) {
2015-04-04 21:46:31 +08:00
case E_ERROR : // 1 //
return 'E_ERROR' ;
case E_WARNING : // 2 //
return 'E_WARNING' ;
case E_PARSE : // 4 //
return 'E_PARSE' ;
case E_NOTICE : // 8 //
return 'E_NOTICE' ;
case E_CORE_ERROR : // 16 //
return 'E_CORE_ERROR' ;
case E_CORE_WARNING : // 32 //
return 'E_CORE_WARNING' ;
case E_COMPILE_ERROR : // 64 //
return 'E_COMPILE_ERROR' ;
case E_COMPILE_WARNING : // 128 //
return 'E_COMPILE_WARNING' ;
case E_USER_ERROR : // 256 //
return 'E_USER_ERROR' ;
case E_USER_WARNING : // 512 //
return 'E_USER_WARNING' ;
case E_USER_NOTICE : // 1024 //
return 'E_USER_NOTICE' ;
case E_STRICT : // 2048 //
return 'E_STRICT' ;
case E_RECOVERABLE_ERROR : // 4096 //
return 'E_RECOVERABLE_ERROR' ;
case E_DEPRECATED : // 8192 //
return 'E_DEPRECATED' ;
case E_USER_DEPRECATED : // 16384 //
return 'E_USER_DEPRECATED' ;
}
return " " ;
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Log .
*
2015-04-04 21:46:31 +08:00
* @ param string $msg
* @ return void
*/
2016-09-20 21:27:41 +08:00
public static function log ( $msg )
2015-04-04 21:46:31 +08:00
{
2016-09-20 21:27:41 +08:00
$msg = $msg . " \n " ;
if ( ! self :: $daemonize ) {
2015-04-04 21:46:31 +08:00
echo $msg ;
}
2016-09-20 21:27:41 +08:00
file_put_contents ( self :: $logFile , date ( 'Y-m-d H:i:s' ) . ' ' . 'pid:' . posix_getpid () . ' ' . $msg , FILE_APPEND | LOCK_EX );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Construct .
*
2015-04-04 21:46:31 +08:00
* @ param string $socket_name
2016-09-20 21:27:41 +08:00
* @ param array $context_option
2015-04-04 21:46:31 +08:00
*/
public function __construct ( $socket_name = '' , $context_option = array ())
{
2016-09-20 21:27:41 +08:00
// Save all worker instances.
$this -> workerId = spl_object_hash ( $this );
2015-04-04 21:46:31 +08:00
self :: $_workers [ $this -> workerId ] = $this ;
2016-09-20 21:27:41 +08:00
self :: $_pidMap [ $this -> workerId ] = array ();
// Get autoload root path.
$backtrace = debug_backtrace ();
$this -> _autoloadRootPath = dirname ( $backtrace [ 0 ][ 'file' ]);
// Context for socket.
if ( $socket_name ) {
2015-04-04 21:46:31 +08:00
$this -> _socketName = $socket_name ;
2016-09-20 21:27:41 +08:00
if ( ! isset ( $context_option [ 'socket' ][ 'backlog' ])) {
$context_option [ 'socket' ][ 'backlog' ] = self :: DEFAULT_BACKLOG ;
2015-04-04 21:46:31 +08:00
}
$this -> _context = stream_context_create ( $context_option );
}
2016-09-20 21:27:41 +08:00
// Set an empty onMessage callback.
$this -> onMessage = function () {
};
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Listen port .
*
2015-04-04 21:46:31 +08:00
* @ throws Exception
*/
public function listen ()
{
2016-09-20 21:27:41 +08:00
if ( ! $this -> _socketName || $this -> _mainSocket ) {
2015-04-04 21:46:31 +08:00
return ;
}
2016-09-20 21:27:41 +08:00
// Autoload.
Autoloader :: setRootPath ( $this -> _autoloadRootPath );
$local_socket = $this -> _socketName ;
// Get the application layer communication protocol and listening address.
2015-04-04 21:46:31 +08:00
list ( $scheme , $address ) = explode ( ':' , $this -> _socketName , 2 );
2016-09-20 21:27:41 +08:00
// Check application layer protocol class.
if ( ! isset ( self :: $_builtinTransports [ $scheme ])) {
$scheme = ucfirst ( $scheme );
$this -> protocol = '\\Protocols\\' . $scheme ;
if ( ! class_exists ( $this -> protocol )) {
$this -> protocol = " \\ Workerman \\ Protocols \\ $scheme " ;
if ( ! class_exists ( $this -> protocol )) {
2015-04-04 21:46:31 +08:00
throw new Exception ( " class \\ Protocols \\ $scheme not exist " );
}
}
2016-09-20 21:27:41 +08:00
$local_socket = $this -> transport . " : " . $address ;
} else {
$this -> transport = self :: $_builtinTransports [ $scheme ];
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Flag.
$flags = $this -> transport === 'udp' ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN ;
$errno = 0 ;
$errmsg = '' ;
// SO_REUSEPORT.
if ( $this -> reusePort ) {
stream_context_set_option ( $this -> _context , 'socket' , 'so_reuseport' , 1 );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
if ( $this -> transport === 'unix' ) {
umask ( 0 );
list (, $address ) = explode ( ':' , $this -> _socketName , 2 );
if ( ! is_file ( $address )) {
register_shutdown_function ( function () use ( $address ) {
@ unlink ( $address );
});
}
}
// Create an Internet or Unix domain server socket.
$this -> _mainSocket = stream_socket_server ( $local_socket , $errno , $errmsg , $flags , $this -> _context );
if ( ! $this -> _mainSocket ) {
2015-04-04 21:46:31 +08:00
throw new Exception ( $errmsg );
}
2016-09-20 21:27:41 +08:00
// Try to open keepalive for tcp and disable Nagle algorithm.
if ( function_exists ( 'socket_import_stream' ) && $this -> transport === 'tcp' ) {
$socket = socket_import_stream ( $this -> _mainSocket );
2015-04-04 21:46:31 +08:00
@ socket_set_option ( $socket , SOL_SOCKET , SO_KEEPALIVE , 1 );
2016-09-20 21:27:41 +08:00
@ socket_set_option ( $socket , SOL_TCP , TCP_NODELAY , 1 );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Non blocking.
2015-04-04 21:46:31 +08:00
stream_set_blocking ( $this -> _mainSocket , 0 );
2016-09-20 21:27:41 +08:00
// Register a listener to be notified when server socket is ready to read.
if ( self :: $globalEvent ) {
if ( $this -> transport !== 'udp' ) {
2015-04-04 21:46:31 +08:00
self :: $globalEvent -> add ( $this -> _mainSocket , EventInterface :: EV_READ , array ( $this , 'acceptConnection' ));
2016-09-20 21:27:41 +08:00
} else {
self :: $globalEvent -> add ( $this -> _mainSocket , EventInterface :: EV_READ ,
array ( $this , 'acceptUdpConnection' ));
2015-04-04 21:46:31 +08:00
}
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Get socket name .
*
2015-04-04 21:46:31 +08:00
* @ return string
*/
public function getSocketName ()
{
2016-09-20 21:27:41 +08:00
return $this -> _socketName ? lcfirst ( $this -> _socketName ) : 'none' ;
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Run worker instance .
*
* @ return void
2015-04-04 21:46:31 +08:00
*/
public function run ()
{
2016-09-20 21:27:41 +08:00
//Update process state.
self :: $_status = self :: STATUS_RUNNING ;
// Register shutdown function for checking errors.
2015-04-04 21:46:31 +08:00
register_shutdown_function ( array ( " \\ Workerman \\ Worker " , 'checkErrors' ));
2016-09-20 21:27:41 +08:00
// Set autoload root path.
Autoloader :: setRootPath ( $this -> _autoloadRootPath );
// Create a global event loop.
if ( ! self :: $globalEvent ) {
$eventLoopClass = " \\ Workerman \\ Events \\ " . ucfirst ( self :: getEventLoopName ());
self :: $globalEvent = new $eventLoopClass ;
// Register a listener to be notified when server socket is ready to read.
if ( $this -> _socketName ) {
if ( $this -> transport !== 'udp' ) {
self :: $globalEvent -> add ( $this -> _mainSocket , EventInterface :: EV_READ ,
array ( $this , 'acceptConnection' ));
} else {
self :: $globalEvent -> add ( $this -> _mainSocket , EventInterface :: EV_READ ,
array ( $this , 'acceptUdpConnection' ));
2015-04-04 21:46:31 +08:00
}
}
}
2016-09-20 21:27:41 +08:00
// Reinstall signal.
2015-04-04 21:46:31 +08:00
self :: reinstallSignal ();
2016-09-20 21:27:41 +08:00
// Init Timer.
2015-04-04 21:46:31 +08:00
Timer :: init ( self :: $globalEvent );
2016-09-20 21:27:41 +08:00
// Try to emit onWorkerStart callback.
if ( $this -> onWorkerStart ) {
try {
call_user_func ( $this -> onWorkerStart , $this );
} catch ( \Exception $e ) {
self :: log ( $e );
exit ( 250 );
} catch ( \Error $e ) {
self :: log ( $e );
exit ( 250 );
}
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Main loop.
2015-04-04 21:46:31 +08:00
self :: $globalEvent -> loop ();
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* Stop current worker instance .
*
2015-04-04 21:46:31 +08:00
* @ return void
*/
public function stop ()
{
2016-09-20 21:27:41 +08:00
// Try to emit onWorkerStop callback.
if ( $this -> onWorkerStop ) {
try {
call_user_func ( $this -> onWorkerStop , $this );
} catch ( \Exception $e ) {
self :: log ( $e );
exit ( 250 );
} catch ( \Error $e ) {
self :: log ( $e );
exit ( 250 );
}
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
// Remove listener for server socket.
2015-04-04 21:46:31 +08:00
self :: $globalEvent -> del ( $this -> _mainSocket , EventInterface :: EV_READ );
@ fclose ( $this -> _mainSocket );
}
/**
2016-09-20 21:27:41 +08:00
* Accept a connection .
*
* @ param resource $socket
2015-04-04 21:46:31 +08:00
* @ return void
*/
public function acceptConnection ( $socket )
{
2016-09-20 21:27:41 +08:00
// Accept a connection on server socket.
$new_socket = @ stream_socket_accept ( $socket , 0 , $remote_address );
// Thundering herd.
if ( ! $new_socket ) {
2015-04-04 21:46:31 +08:00
return ;
}
2016-09-20 21:27:41 +08:00
// TcpConnection.
$connection = new TcpConnection ( $new_socket , $remote_address );
$this -> connections [ $connection -> id ] = $connection ;
$connection -> worker = $this ;
$connection -> protocol = $this -> protocol ;
$connection -> onMessage = $this -> onMessage ;
$connection -> onClose = $this -> onClose ;
$connection -> onError = $this -> onError ;
$connection -> onBufferDrain = $this -> onBufferDrain ;
$connection -> onBufferFull = $this -> onBufferFull ;
// Try to emit onConnect callback.
if ( $this -> onConnect ) {
try {
2015-04-04 21:46:31 +08:00
call_user_func ( $this -> onConnect , $connection );
2016-09-20 21:27:41 +08:00
} catch ( \Exception $e ) {
self :: log ( $e );
exit ( 250 );
} catch ( \Error $e ) {
2015-04-04 21:46:31 +08:00
self :: log ( $e );
2016-09-20 21:27:41 +08:00
exit ( 250 );
2015-04-04 21:46:31 +08:00
}
}
}
2016-09-20 21:27:41 +08:00
2015-04-04 21:46:31 +08:00
/**
2016-09-20 21:27:41 +08:00
* For udp package .
*
2015-04-04 21:46:31 +08:00
* @ param resource $socket
2016-09-20 21:27:41 +08:00
* @ return bool
2015-04-04 21:46:31 +08:00
*/
public function acceptUdpConnection ( $socket )
{
2016-09-20 21:27:41 +08:00
$recv_buffer = stream_socket_recvfrom ( $socket , self :: MAX_UDP_PACKAGE_SIZE , 0 , $remote_address );
if ( false === $recv_buffer || empty ( $remote_address )) {
2015-04-04 21:46:31 +08:00
return false ;
}
2016-09-20 21:27:41 +08:00
// UdpConnection.
$connection = new UdpConnection ( $socket , $remote_address );
$connection -> protocol = $this -> protocol ;
if ( $this -> onMessage ) {
if ( $this -> protocol ) {
$parser = $this -> protocol ;
$recv_buffer = $parser :: decode ( $recv_buffer , $connection );
2015-04-04 21:46:31 +08:00
}
2016-09-20 21:27:41 +08:00
ConnectionInterface :: $statistics [ 'total_request' ] ++ ;
try {
call_user_func ( $this -> onMessage , $connection , $recv_buffer );
} catch ( \Exception $e ) {
self :: log ( $e );
exit ( 250 );
} catch ( \Error $e ) {
self :: log ( $e );
exit ( 250 );
2015-04-04 21:46:31 +08:00
}
}
2016-09-20 21:27:41 +08:00
return true ;
2015-04-04 21:46:31 +08:00
}
}