主從reactor模式

muduo中,類EventLoopThreadPool的構造函數將成員numThreads_設置爲0,表示默認不開啓主從Reactor模式,即單Reactor模式。
Reactor模式中,該Reactor負責監聽新連接的到來、套接字的可讀可寫。

通過在調用void TcpServer::start()之前,調用EventLoopThreadPool::setThreadNum()。即可開啓主從Reactor模式

TcpServer的結構如下:

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章