当前位置:首页 >> 编程开发 >> Visual C++ >> 内容

Muduo 网络编程示例(六)限制服务器的最大并发连接数

时间:2015/5/19 21:32:04 作者:平凡之路 来源:xuhantao.com 浏览:

本文已以大家都熟悉的 EchoServer 介绍如何限制服务器的并发连接数。

本文的代码见 http://code.google.com/p/muduo/source/browse/trunk/examples/maxconnection/

《Muduo 网络 编程示例 系列》计划中的第六篇文章原本是“用于测试两台机器的带宽的 pingpong 程序”, pingpong 协议的程序已经在《muduo 与 boost asio 吞吐量对比》和《muduo 与 libevent2 吞吐量对 比》两篇文章中介绍过了,所以我改为写另外一个有点意思的主题。

这篇文章中的“并发连接 数”是指一个 server program 能同时支持的客户端连接数,连接系由客户端主动发起,服务端被动接 受(accept)连接。(如果要限制应用程序主动发起的连接,则问题要简单得多,毕竟主动权和决定权都 在程序本身。)

为什么要限制并发连接数?

一方面,我们不希望服务程序超载,另一方 面,更因为 file descriptor 是稀缺资源,如果出现 file descriptor 耗尽,很棘手(跟 “malloc 失败/new() 抛出 std::bad_alloc”差不多同样棘手)。

我在《分布式系统的工程化开发方法 》一文中曾谈到 libev 作者建议的一种应对“accept()ing 时 file descriptor 耗尽”的办法。

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 解决。

查看本栏目更多精彩内容:http://www.bianceng.cn/Programming/cplus/

相关文章
  • 没有相关文章
共有评论 0相关评论
发表我的评论
  • 大名:
  • 内容:
  • 徐汉涛(www.xuhantao.com) © 2024 版权所有 All Rights Reserved.
  • 部分内容来自网络,如有侵权请联系站长尽快处理 站长QQ:965898558(广告及站内业务受理) 网站备案号:蒙ICP备15000590号-1