在我们启动了workerman过后,按照我们前面的理解.如果是在linux下.worker子进程启动端口复用,并监听事件和处理事件(win忽略).那workerman是如何对事件完成监听和处理的呢.我们来看一下.
worker listen
在我们看源码的时候,在forkOneWorkerForLinux有这样一行代码.
if ($worker->reusePort) {
$worker->listen();}
这里,一看名字就知道是对端口进行监听.我们来看一下源码
public function listen()
{
// 这里的socketName就是我们监听的地址信息.
if (!$this->_socketName) { return; }
// 设置自动加载的信息.
Autoloader::setRootPath($this->_autoloadRootPath);
// 需要监听的socket.
if (!$this->_mainSocket) {
// 解析socket地址.
$local_socket = $this->parseSocketAddress();
// 设置基础协议.如果是udp.就为STREAM_SERVER_BIND,如果不是udp就是,就是4 | 8.注意,这里是按位或算法.则为 100(4) | 1000(8) = 1100.
$flags = $this->transport === 'udp' ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $errno = 0; $errmsg = '';
// 设置端口复用.
if ($this->reusePort) {
\stream_context_set_option($this->\context, 'socket', 'so_reuseport', 1);
}
// 开始创建socket.
$this->_mainSocket = \stream_socket_server($local_socket, $errno, $errmsg, $flags, $this->_context);
if (!$this->_mainSocket) {
throw new Exception($errmsg);
}
// 如果transport为ssl,就要特殊处理.
if ($this->transport === 'ssl') {
\stream_socket_enable_crypto($this->_mainSocket, false);
} elseif ($this->transport === 'unix') {
// 如果是unix.
$socket_file = \substr($local_socket, 7);
if ($this->user) {
chown($socket_file, $this->user);
}
if ($this->group) {
chgrp($socket_file, $this->group);
}
}
// 尝试打开tcp的keepalive并禁用掉Nagle算法.
if (\function_exists('socket_import_stream') && static::$_builtinTransports[$this->transport] === 'tcp') {
\set_error_handler(function(){});
$socket = \socket_import_stream($this->_mainSocket);
\socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
\socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1);
\restore_error_handler();
} // 为资源流设置为非阻塞模式.
\stream_set_blocking($this->_mainSocket, 0);
}
// 设置监听.
$this->resumeAccept();}
在这里,workerman首先是创建了一个_mainSocket的socket监听信息.并设置为非阻塞模式.并设置监听.
public function resumeAccept()
{
// 设置事件监听.
if (static::$globalEvent && true === $this->_pauseAccept && $this->_mainSocket) {
// 如果不是udp协议.就采用acceptConnection来才听.
if ($this->transport !== 'udp') {
static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection'));
} else {
// udp协议,就采用acceptUdpConnection来监听
static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection'));
}
// 设置pauseAccept为false.
$this->_pauseAccept = false;
}
}
我们来看看acceptConnection.
public function acceptConnection($socket)
{
// 设置错误信息.
\set_error_handler(function(){});
// 接收信息.
$new_socket = stream_socket_accept($socket, 0, $remote_address);
// 恢复错误信息.
\restore_error_handler();
// 如果没有接收成功.
if (!$new_socket) { return; }
// 底层采用Tcp来监听.
$connection = new TcpConnection($new_socket, $remote_address);
// 设置connections来监听.
$this->connections[$connection->id] = $connection;
// 把worker复制进去.
$connection->worker = $this;
$connection->protocol = $this->protocol;
$connection->transport = $this->transport;
// 设置事件监听.
$connection->onMessage = $this->onMessage;
// 关闭链接时的处理.
$connection->onClose = $this->onClose;
// 设置错误信息.
$connection->onError = $this->onError;
$connection->onBufferDrain = $this->onBufferDrain;
$connection->onBufferFull = $this->onBufferFull;
// 如果有onConnect连接处理.就直接调用.
if ($this->onConnect) {
try {
\call_user_func($this->onConnect, $connection);
} catch (\Exception $e) {
static::log($e);
exit(250);
} catch (\Error $e) {
static::log($e);
exit(250);
}
}
}
这里,我们就完成了对事件的监听.并且每个链接下面都将worker的信息赋值给TcpConnection的worker中.所以,每一个Connection都会有worker信息.完成了事件的监听.那如何处理事件的呢.
event loop
我们在worker的run方法中,有这样一句话.
static::$globalEvent->loop();
这里采用了死循环来等待来处理事件.我们在前面看了.$globalEvent是采用workerman自己本身的来监听.它是在run方法中初始化的.
// run
if (!static::$globalEvent) {
$event_loop_class = static::getEventLoopName();
static::$globalEvent = new $event_loop_class;
$this->resumeAccept();
}
// Worker::getEventLoopName()
protected static function getEventLoopName()
{
// 如果存在就直接返回.
if (static::$eventLoopClass) {
return static::$eventLoopClass;
}
// 如果存在就删除Swoole\Event.
if (!\class_exists('\Swoole\Event', false)) {
unset(static::$_availableEventLoops['swoole']);
}
$loop_name = '';
// protected static $\_availableEventLoops = array(
// 'libevent' => '\\Workerman\\Events\\Libevent',
// 'event' => '\\Workerman\\Events\\Event'
// 'swoole' => '\\Workerman\\Events\\Swoole'
// );
foreach (static::$\_availableEventLoops as $name=>$class) {
if (\extension_loaded($name)) {
$loop_name = $name;
break;
}
}
if ($loop\_name) {
// 如果存在名称.就使用React下面的事件处理.
if (\interface_exists('\React\EventLoop\LoopInterface')) {
switch ($loop_name) {
case 'libevent':
static::$eventLoopClass = '\Workerman\Events\React\ExtLibEventLoop';
break;
case 'event':
static::$eventLoopClass = '\Workerman\Events\React\ExtEventLoop';
break;
default :
static::$eventLoopClass = '\Workerman\Events\React\StreamSelectLoop';
break;
}
} else {
// 就默认. 我们这里采用的Libevent.
static::$eventLoopClass = static::$\_availableEventLoops[$loop_name];
}
} else {
static::$eventLoopClass = \interface_exists('\React\EventLoop\LoopInterface')? '\Workerman\Events\React\StreamSelectLoop':'\Workerman\Events\Select';
}
return static::$eventLoopClass;
}
以上就是初始化$globalEvent.我当前的环境是libevent.所以我们就看一下.WorkermanEventsLibevent的loop方法.
public function loop()
{
// 调用了一个event_base_loop.
\event_base_loop($this->_eventBase);
}
只有一个方法.event_base_loop就等待事件被触发,然后触发他们的事件.这里的是$this->_eventBase.而设置事件则在我们的add方法中.在listen的时候,我们调用了如下的方法.
static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection'));
所以我们来看一下add.
public function add($fd, $flag, $func, $args = array())
{
// 判断信息.
switch ($flag) {
case self::EV_SIGNAL:
$fd_key = (int)$fd; $real_flag = EV_SIGNAL | EV_PERSIST;
$this->_eventSignal[$fd_key] = event_new();
if (!\event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) {
return false;
}
if (!\event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) {
return false;
}
if (!\event_add($this->_eventSignal[$fd_key])) {
return false;
}
return true;
case self::EV_TIMER:
case self::EV_TIMER_ONCE:
$event = \event_new();
$timer_id = (int)$event;
if (!\event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) {
return false;
}
if (!\event_base_set($event, $this->_eventBase)) {
return false;
}
$time_interval = $fd * 1000000;
if (!\event_add($event, $time_interval)) {
return false;
}
$this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval);
return $timer_id;
default :
$fd_key = (int)$fd;
$real_flag = $flag === self::EV_READ ? EV_READ | EV_PERSIST : EV_WRITE | EV_PERSIST;
$event = \event_new();
if (!\event_set($event, $fd, $real_flag, $func, null)) {
return false;
}
if (!\event_base_set($event, $this->_eventBase)) {
return false;
}
if (!\event_add($event)) {
return false;
}
$this->_allEvents[$fd_key][$flag] = $event;
return true;
}
}
我们从以上方法可以精简出大概的几个语句
// 创建一个新的事件.
$event = \event_new();
// 设置事件的监听.
if (!\event_set($event, $fd, $real_flag, $func, null)) {
return false;
}
// 重新设置event的绑定事件.
if (!\event_base_set($event, $this->_eventBase)) {
return false;
}
// 添加事件.
if (!\event_add($event)) {
return false;
}
// 保存到全局的事件中去.
$this->_allEvents[$fd_key][$flag] = $event;
所以.我们在run方法中,先设置事件的监听.然后在调用事件的处理.而完成事件的处理则是由我们worker自带的onMessage方法来处理.
最后
event_set和event_base_set的作用.
workerman 对 worker的监控.