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;
}