workerman知识清单
Worker类:静态Worker统一管理实例worker
$this->workerId = \spl_object_hash($this); static::$_workers[$this->workerId] = $this; static::$_pidMap[$this->workerId] = array();
进程端口复用原理
参考:https://www.jianshu.com/p/97cc8c52d47a
惊群效应
为了提升性能,一般的服务端程序在运行时都有多个进程(俗称 Worker)监听同一个 Socket,在没有客户端连接到来的时候,这些Worker是处于挂起状态的,不消耗CPU资源。
只有一个 Worker 可以获得处理这个连接的机会,其他Worker在竞争失败后继续回到挂起状态。唤醒 Worker 的过程是要消耗CPU资源的,Worker 数量越多,消耗的 CPU 资源就越多,造成了资源的浪费。这就是常说的 惊群效应。
代码细节
#Worker
类是 Workerman 里最主要的类,其中有个listen()
函数
protected function listen() { ... if (!$this->_mainSocket) { ... $this->_mainSocket = stream_socket_server(...); ... } ... }
#当 reusePort 为 false 时,主进程在创建 Worker 之前就调用了listen()
函数 protected function initWorkers() { .... if (!$worker->reusePort) { $worker->listen(); } .... }
#当 reusePort 为 true 时,情况就不同了。主进程在创建 Worker 前不会调用listen()
,而是在创建完 Worker 后由每个 Worker 自行发起listen()
调用 protected static function forkOneWorkerForLinux($worker) { ... $pid = pcntl_fork(); if ($pid === 0) { if ($worker->reusePort) { $worker->listen(); } ... } ... }
#想要内核开启 reuseport 功能,需要手动设置 Socket 的 context if ($this->reusePort) { $context = stream_context_create(); stream_context_set_option($context, 'socket', 'so_reuseport', 1); }
定时器
1,定时器是用来实现定时任务的,分为持续间隔计时(类似setTimeInterval())和一次性计时(类似setTimeOut())
2,定时器实现有两种方式:使用事件和任务数组
//初始化
Timer::init(static::$globalEvent); if ($event) { self::$_event = $event; return; } if (\function_exists('pcntl_signal')) { \pcntl_signal(\SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false); }
//添加定时任务 Timer::add(static::KILL_WORKER_TIMER_TIME, '\posix_kill', array($one_worker_pid, \SIGKILL), false); if (self::$_event) { return self::$_event->add($time_interval, $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); } if (!\is_callable($func)) { Worker::safeEcho(new Exception("not callable")); return false; } if (empty(self::$_tasks)) { \pcntl_alarm(1); } $run_time = \time() + $time_interval; if (!isset(self::$_tasks[$run_time])) { self::$_tasks[$run_time] = array(); } self::$_timerId = self::$_timerId == \PHP_INT_MAX ? 1 : ++self::$_timerId; self::$_status[self::$_timerId] = true; self::$_tasks[$run_time][self::$_timerId] = array($func, (array)$args, $persistent, $time_interval);
//执行任务 Timer::tick() if (self::$_event) { return self::$_event->add($time_interval, $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); } $time_now = \time(); foreach (self::$_tasks as $run_time => $task_data) { if ($time_now >= $run_time) { foreach ($task_data as $index => $one_task) { $task_func = $one_task[0]; $task_args = $one_task[1]; $persistent = $one_task[2]; $time_interval = $one_task[3]; try { \call_user_func_array($task_func, $task_args); } catch (\Exception $e) { Worker::safeEcho($e); } if($persistent && !empty(self::$_status[$index])) { $new_run_time = \time() + $time_interval; if(!isset(self::$_tasks[$new_run_time])) self::$_tasks[$new_run_time] = array(); self::$_tasks[$new_run_time][$index] = array($task_func, (array)$task_args, $persistent, $time_interval); } } unset(self::$_tasks[$run_time]); } }
事件
有3种事件:
1,信号事件:
$this->_eventSignal
2,socket io事件
$this->_allEvents
3,定时器事件
$this->_eventTimer
//绑定事件 if ($this->transport !== 'udp') { static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection')); } else { static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection')); } // reinstall stop signal handler static::$globalEvent->add(\SIGINT, EventInterface::EV_SIGNAL, $signalHandler); // reinstall graceful stop signal handler static::$globalEvent->add(\SIGHUP, EventInterface::EV_SIGNAL, $signalHandler); // reinstall reload signal handler static::$globalEvent->add(\SIGUSR1, EventInterface::EV_SIGNAL, $signalHandler); // reinstall graceful reload signal handler static::$globalEvent->add(\SIGQUIT, EventInterface::EV_SIGNAL, $signalHandler); // reinstall status signal handler static::$globalEvent->add(\SIGUSR2, EventInterface::EV_SIGNAL, $signalHandler); // reinstall connection status signal handler static::$globalEvent->add(\SIGIO, EventInterface::EV_SIGNAL, $signalHandler); //事件轮询 static::$globalEvent->loop();//系统自动触发 //解绑事件 static::$globalEvent->del($this->_mainSocket, EventInterface::EV_READ); //销毁所有事件 static::$globalEvent->destroy(); //统计事件 static::$globalEvent->getTimerCount(); //定时器事件方法 \Workerman\Timer::$_event->clearAllTimer(); static::$globalEvent->getTimerCount()