muduo
中,类EventLoopThreadPool
的构造函数将成员numThreads_
设置为0,表示默认不开启主从Reactor模式,即单Reactor模式。
单Reactor
模式中,该Reactor
负责监听新连接的到来、套接字的可读可写。
通过在调用void TcpServer::start()
之前,调用EventLoopThreadPool::setThreadNum()
。即可开启主从Reactor模式。
类TcpServe
r的结构如下:
class TcpServer : noncopyable
{
public:
...
private:
EventLoop* loop_; // the acceptor loop
...
std::shared_ptr<EventLoopThreadPool> threadPool_;
...
};
TcpServer
构造时,传入已经构造好的EventLoop对象赋值给成员loop_
,loop_
运行在主线程中。
称这个EventLoop
为主Reactor,只会负责监听新的连接请求。
TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option)
: loop_(CHECK_NOTNULL(loop)), // 外部直接传入
ipPort_(listenAddr.toIpPort()),
name_(nameArg),
acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)), // 将loop_作为主reactor使用
threadPool_(new EventLoopThreadPool(loop, name_)),
connectionCallback_(defaultConnectionCallback),
messageCallback_(defaultMessageCallback),
nextConnId_(1)
{
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, _1, _2));
}
服务器启动时,会调用TcpServer::start()
,其中又会调用EventLoopThreadPool::start(const ThreadInitCallback& cb)
,用来初始化并运行子线程并保存在TcpServer::threadPool_
中,这些子线程中运行着EventLoop
的无限事件循环。称这些运行在EventLoopThread
中的EventLoop
为从Reactor。
void TcpServer::start()
{
if (started_.getAndSet(1) == 0)
{
threadPool_->start(threadInitCallback_);
assert(!acceptor_->listenning());
loop_->runInLoop(
std::bind(&Acceptor::listen, get_pointer(acceptor_)));
}
}
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
assert(!started_);
baseLoop_->assertInLoopThread();
started_ = true;
for (int i = 0; i < numThreads_; ++i)
{
char buf[name_.size() + 32];
snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);
EventLoopThread* t = new EventLoopThread(cb, buf);
threads_.push_back(std::unique_ptr<EventLoopThread>(t));
loops_.push_back(t->startLoop());
}
if (numThreads_ == 0 && cb)
{
cb(baseLoop_);
}
}
对于已经连接的套接字,监听它们的事件,由从Reactor负责, 也即是运行在子线程中的EventLoop负责。
当需要一个从Reactor时,需要调用EventLoopThreadPool->getNextLoop()
;
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
EventLoop* ioLoop = threadPool_->getNextLoop(); // 这里获取一个从reactor
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd));
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
...;
}
EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
assert(started_);
EventLoop* loop = baseLoop_;
if (!loops_.empty())
{
// round-robin
loop = loops_[next_];
++next_;
if (implicit_cast<size_t>(next_) >= loops_.size())
{
next_ = 0;
}
}
return loop;
}