Master-slave reactor mode

muduoIn EventLoopThreadPoolthe constructor of the class , the member is numThreads_set to 0, which means that the master-slave Reactor mode is not turned on by default , that is, the single Reactor mode.
In single Reactormode, it Reactoris responsible for monitoring the arrival of new connections and the read and write of the socket.

Pass before the call void TcpServer::start(), call EventLoopThreadPool::setThreadNum(). You can turn on the master-slave Reactor mode .

TcpServeThe structure of class r is as follows:

class TcpServer : noncopyable
{
    
    
public:
	...
private:
	EventLoop* loop_;  // the acceptor loop
	...
	std::shared_ptr<EventLoopThreadPool> threadPool_;
	...
};

TcpServerWhen constructing, the constructed EventLoop object is passed in and assigned to the member loop_, which loop_runs in the main thread.
Saying it 个EventLoopis the main the Reactor , only responsible for monitoring new connection requests .

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

When the server starts, it will be called TcpServer::start(), and it will be called again EventLoopThreadPool::start(const ThreadInitCallback& cb)to initialize and run the child threads and save them in the infinite event loop TcpServer::threadPool_running in these child threads EventLoop. Call these runs EventLoopThreadare EventLoopas from the 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_);
	}
}

For connected sockets, monitoring their events is the responsibility of the slave Reactor, that is, the EventLoop running in the child thread .
When a slave Reactor is needed, it needs to be called 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;
}

Guess you like

Origin blog.csdn.net/xp178171640/article/details/106073528