Muduo网络编程示例之六:限易做图务器的最大并发连接数
为什么要限制并发连接数?
一方面,我们不希望服务程序超载,另一方面,更因为 file descriptor 是稀缺资源,如果出现 file descriptor 耗尽,很棘手(跟 “malloc 失败/new() 抛出 std::bad_alloc”差不多同样棘手)。
Muduo 的 acceptor 正是这么实现的,但是,这个做法在多线程下不能保证正确,会有 race condition。(思考题:是什么 race condition?)
其实有另外一种比较简单的办法:file descriptor 是 hard limit,我们可以自己设一个稍低一点的 soft limit,如果超过 soft limit 就主动关闭新连接,这样就避免触及“file descriptor 耗尽”这种边界条件。比方说当前进程的 max file descriptor 是 1024,那么我们可以在连接数达到 1000 的时候进入“拒绝新连接”状态,这样留给我们足够的腾挪空间。
Muduo 中限制并发连接数
Muduo 中限制并发连接数的做法简单得出奇。以在《Muduo 网络编程示例之零:前言》中出场过的 EchoServer 为例,只需要为它增加一个 int 成员,表示当前的活动连接数。(如果是多线程程序,应该用 muduo::AtomicInt32。)
class EchoServer
{
public:
EchoServer(muduo::net::EventLoop* loop,
const muduo::net::InetAddress& listenAddr,
int maxConnections);void start();
private:
void onConnection(const muduo::net::TcpConnectionPtr& conn);void onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp time);muduo::net::EventLoop* loop_;
muduo::net::TcpServer server_;
int numConnected_; // should be atomic_int
const int kMaxConnections;
};
然后,在 EchoServer::onConnection() 中判断当前活动连接数,如果超过最大允许数,则踢掉连接。void EchoServer::onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "EchoServer - " << conn->peerAddress().toHostPort() << " -> "
<< conn->localAddress().toHostPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");if (conn->connected())
{
++numConnected_;
if (numConnected_ > kMaxConnections)
{
conn->shutdown();
}
}
else
{
--numConnected_;
}
LOG_INFO << "numConnected = " << numConnected_;
}
这种做法可以积极地防止耗尽 file descriptor。另外,如果是有业务逻辑的服务,可以在 shutdown() 之前发送一个简单的响应,表明本服务程序的负载能力已经饱和,提示客户端尝试下一个可用的 server(当然,下一个可用的 server 地址不一定要在这个响应里给出,客户端可以自己去 name service 查询),这样方便客户端快速 failover。
后文将介绍如何处理空闲连接的超时:如果一个连接长时间(若干秒)没有输入数据,则踢掉此连接。办法有很多种,我用 Time Wheel 解决。
补充:软件开发 , C语言 ,