workerman events.php,workerman源码-workerman事件监听

在我们启动了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的监控.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值