文件描述符是稀缺資源。若耗盡(觸及 ulimit -n 硬限制),程式將無法接受新連線、開啟檔案、建立 Socket,處理邊界條件極其棘手(類似 malloc 失敗)。採用「軟限制」策略:預留餘量(如硬限 1024,軟限 1000),超過軟限即主動拒絕新連線,避免觸及硬邊界。
以 EchoServer 為例,新增 numConnected_ 計數器與 kMaxConnections 上限:
class EchoServer
{
public:
EchoServer(EventLoop* loop, const InetAddress& listenAddr, int maxConnections);
void start();
private:
void onConnection(const TcpConnectionPtr& conn);
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time);
EventLoop* loop_;
TcpServer server_;
int numConnected_; // 多線程下應用 AtomicInt32
const int kMaxConnections;
};
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_;
}
在 shutdown() 前可發送自定義響應(如「服務飽和,請嘗試其他節點」),引導客戶端快速切換,配合命名服務實現自動 Failover。
Acceptor 內部判斷連接數再接受的做法在多線程下存在競態條件(思考題:是什麼 race condition?)。採用應用層計數器 + 原子操作更簡單可靠。
下一篇將介紹如何用 Timing Wheel 踢掉長時間無資料傳輸的空閒連線。