diff --git a/docs/cn/client.md b/docs/cn/client.md index b9fe872dd8..609becc086 100755 --- a/docs/cn/client.md +++ b/docs/cn/client.md @@ -820,8 +820,9 @@ brpc支持[Streaming RPC](streaming_rpc.md),这是一种应用层的连接, | Name | Value | Description | Defined At | | ------------------ | ----- | ---------------------------------------- | ----------------------- | | defer_close_second | 0 | Defer close of connections for so many seconds even if the connection is not used by anyone. Close immediately for non-positive values | src/brpc/socket_map.cpp | +| defer_close_respect_idle | false | 当 defer_close_second > 0 时,如果连接在最后一个引用释放时已经闲置超过 defer_close_second,则立刻关闭连接(默认关闭以保持兼容) | src/brpc/socket_map.cpp | -设置后引用计数清0时连接并不会立刻被关闭,而是会等待这么多秒再关闭,如果在这段时间内又有channel引用了这个连接,它会恢复正常被使用的状态。不管channel创建析构有多频率,这个选项使得关闭连接的频率有上限。这个选项的副作用是一些fd不会被及时关闭,如果延时被误设为一个大数值,程序占据的fd个数可能会很大。 +设置后引用计数清0时连接并不会立刻被关闭,而是会等待这么多秒再关闭,如果在这段时间内又有channel引用了这个连接,它会恢复正常被使用的状态。不管channel创建析构有多频率,这个选项使得关闭连接的频率有上限。这个选项的副作用是一些fd不会被及时关闭,如果延时被误设为一个大数值,程序占据的fd个数可能会很大。开启 -defer_close_respect_idle 后,如果连接在最后一个引用释放时已经闲置超过 defer_close_second,则可能会被关闭。 ## 连接的缓冲区大小 diff --git a/docs/en/client.md b/docs/en/client.md index f199fc6b78..60c458b62c 100644 --- a/docs/en/client.md +++ b/docs/en/client.md @@ -717,8 +717,9 @@ Another solution is setting gflag -defer_close_second | Name | Value | Description | Defined At | | ------------------ | ----- | ---------------------------------------- | ----------------------- | | defer_close_second | 0 | Defer close of connections for so many seconds even if the connection is not used by anyone. Close immediately for non-positive values | src/brpc/socket_map.cpp | +| defer_close_respect_idle | false | When defer_close_second > 0, close a connection immediately when the last reference is removed and the socket has already been idle for longer than defer_close_second | src/brpc/socket_map.cpp | -After setting, connection is not closed immediately after last referential count, instead it will be closed after so many seconds. If a channel references the connection again during the wait, the connection resumes to normal. No matter how frequent channels are created, this flag limits the frequency of closing connections. Side effect of the flag is that file descriptors are not closed immediately after destroying of channels, if the flag is wrongly set to be large, number of active file descriptors in the process may be large as well. +After setting, connection is not closed immediately after last referential count, instead it will be closed after so many seconds. If a channel references the connection again during the wait, the connection resumes to normal. No matter how frequent channels are created, this flag limits the frequency of closing connections. Side effect of the flag is that file descriptors are not closed immediately after destroying of channels, if the flag is wrongly set to be large, number of active file descriptors in the process may be large as well. When -defer_close_respect_idle is enabled, a connection that has already been idle for longer than defer_close_second may be closed when the last reference is removed. ## Buffer size of connections diff --git a/src/brpc/socket_map.cpp b/src/brpc/socket_map.cpp index 3984f6b866..8d934f5827 100644 --- a/src/brpc/socket_map.cpp +++ b/src/brpc/socket_map.cpp @@ -46,6 +46,13 @@ DEFINE_int32(defer_close_second, 0, "non-positive values."); BRPC_VALIDATE_GFLAG(defer_close_second, PassValidate); +DEFINE_bool(defer_close_respect_idle, false, + "When defer_close_second > 0, close a connection immediately when " + "the last reference is removed and the socket has already been " + "idle for longer than defer_close_second. Disabled by default for " + "backward compatibility."); +BRPC_VALIDATE_GFLAG(defer_close_respect_idle, PassValidate); + DEFINE_bool(show_socketmap_in_vars, false, "[DEBUG] Describe SocketMaps in /vars"); BRPC_VALIDATE_GFLAG(show_socketmap_in_vars, PassValidate); @@ -71,6 +78,7 @@ static void CreateClientSideSocketMap() { options.socket_creator = new GlobalSocketCreator; options.idle_timeout_second_dynamic = &FLAGS_idle_timeout_second; options.defer_close_second_dynamic = &FLAGS_defer_close_second; + options.defer_close_respect_idle_dynamic = &FLAGS_defer_close_respect_idle; if (socket_map->Init(options) != 0) { LOG(FATAL) << "Fail to init SocketMap"; exit(1); @@ -130,7 +138,8 @@ SocketMapOptions::SocketMapOptions() , idle_timeout_second_dynamic(NULL) , idle_timeout_second(0) , defer_close_second_dynamic(NULL) - , defer_close_second(0) { + , defer_close_second(0) + , defer_close_respect_idle_dynamic(NULL) { } SocketMap::SocketMap() @@ -296,15 +305,29 @@ void SocketMap::RemoveInternal(const SocketMapKey& key, *_options.defer_close_second_dynamic : _options.defer_close_second; if (!remove_orphan && defer_close_second > 0) { - // Start count down on this Socket - sc->no_ref_us = butil::cpuwide_time_us(); - } else { - Socket* const s = sc->socket; - _map.erase(key); - mu.unlock(); - s->ReleaseAdditionalReference(); // release extra ref - ReleaseReference(s); + const int64_t now_us = butil::cpuwide_time_us(); + // NOTE: save the gflag which may be reloaded at any time + const bool defer_close_respect_idle = _options.defer_close_respect_idle_dynamic ? + *_options.defer_close_respect_idle_dynamic : false; + if (!defer_close_respect_idle) { + // Start count down on this Socket. + sc->no_ref_us = now_us; + return; + } + const int64_t defer_us = (int64_t)defer_close_second * 1000000L; + if (sc->no_ref_us <= sc->socket->last_active_time_us() + defer_us) { + // When defer_close_respect_idle is enabled, a connection that has + // already been idle for longer than defer_close_second is closed + // immediately. + sc->no_ref_us = now_us; + return; + } } + Socket* const s = sc->socket; + _map.erase(key); + mu.unlock(); + s->ReleaseAdditionalReference(); // release extra ref + ReleaseReference(s); } } diff --git a/src/brpc/socket_map.h b/src/brpc/socket_map.h index b1922bf86e..c4dc5c28f3 100644 --- a/src/brpc/socket_map.h +++ b/src/brpc/socket_map.h @@ -154,6 +154,14 @@ struct SocketMapOptions { // Default: 0 (disabled) const int* defer_close_second_dynamic; int defer_close_second; + + // When defer_close_second > 0 and this flag is true, close a connection + // immediately when the last reference is removed and the socket has already + // been idle for longer than defer_close_second. + // If defer_close_respect_idle_dynamic is not NULL, use the dereferenced + // value each time. + // Default: NULL (treated as false) + const bool* defer_close_respect_idle_dynamic; }; // Share sockets to the same EndPoint.