This is an automated email from the ASF dual-hosted git repository.
guangmingchen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push:
new a103b4b6 Prevent indefinite defer-close by checking last_active_time
(#3216)
a103b4b6 is described below
commit a103b4b6f5ed3186e0bdd8103dbc7835330d7ddf
Author: Jenrry You <[email protected]>
AuthorDate: Tue Mar 10 16:00:23 2026 +0800
Prevent indefinite defer-close by checking last_active_time (#3216)
Co-authored-by: youzhiyuan <[email protected]>
---
docs/cn/client.md | 3 ++-
docs/en/client.md | 3 ++-
src/brpc/socket_map.cpp | 41 ++++++++++++++++++++++++++++++++---------
src/brpc/socket_map.h | 8 ++++++++
4 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/docs/cn/client.md b/docs/cn/client.md
index b9fe872d..609becc0 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 f199fc6b..60c458b6 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 fi [...]
+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 fi [...]
## Buffer size of connections
diff --git a/src/brpc/socket_map.cpp b/src/brpc/socket_map.cpp
index 3984f6b8..8d934f58 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 b1922bf8..c4dc5c28 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.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]