This is an automated email from the ASF dual-hosted git repository.
chenBright 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 f6203937 fix(channel): Reject reinitialization to keep SocketMap
references balanced (#3433)
f6203937 is described below
commit f6203937a983bc5507165883ea09febb42d63773
Author: darion-yaphet <[email protected]>
AuthorDate: Tue Aug 25 19:51:03 2026 +0800
fix(channel): Reject reinitialization to keep SocketMap references balanced
(#3433)
Reject re-initialization once Channel::Init() has succeeded. This ensures
a Channel instance only inserts into SocketMap at most once and preserves
its options and signature intact, guaranteeing that ~Channel() always
balances the insertion without requiring extra state tracking. Failed inits
before the first successful initialization can still be retried.
---
docs/cn/client.md | 3 +
docs/en/client.md | 6 ++
src/brpc/channel.cpp | 16 +++
src/brpc/channel.h | 3 +
test/brpc_channel_unittest.cpp | 222 +++++++++++++++++++++++++++++++----------
5 files changed, 196 insertions(+), 54 deletions(-)
diff --git a/docs/cn/client.md b/docs/cn/client.md
index a3531147..e659403c 100755
--- a/docs/cn/client.md
+++ b/docs/cn/client.md
@@ -7,6 +7,7 @@
Echo的[client端代码](https://github.com/apache/brpc/blob/master/example/echo
# 事实速查
- Channel.Init()是线程不安全的。
+- 一个Channel只能成功初始化一次。Init()失败后可以重试。
- Channel.CallMethod()是线程安全的,一个Channel可以被所有线程同时使用。
- Channel可以分配在栈上。
- Channel在发送异步请求后可以析构。
@@ -32,6 +33,8 @@ channel.Init(..., &options);
```
注意Channel不会修改options,Init结束后不会再访问options。所以options一般就像上面代码中那样放栈上。Channel.options()可以获得channel在使用的所有选项。
+Init失败后可以重试;一旦成功,Channel的目标和选项即固定,之后的所有Init调用都会返回-1。需要使用不同的目标或配置时,请新建一个Channel。
+
Init函数分为连接一台服务器和连接服务集群。
# 连接一台服务器
diff --git a/docs/en/client.md b/docs/en/client.md
index 087c39b5..266eb902 100644
--- a/docs/en/client.md
+++ b/docs/en/client.md
@@ -7,6 +7,8 @@
# Quick facts
- Channel.Init() is not thread-safe.
+- A Channel can be initialized successfully only once. Failed Init() calls may
+ be retried.
- Channel.CallMethod() is thread-safe and a Channel can be used by multiple
threads simultaneously.
- Channel can be put on stack.
- Channel can be destructed just after sending asynchronous request.
@@ -32,6 +34,10 @@ channel.Init(..., &options);
```
Note that Channel neither modifies `options` nor accesses `options` after
completion of Init(), thus options can be put on stack safely as in above code.
Channel.options() gets options being used by the Channel.
+Init() may be retried after a failure. Once it succeeds, the Channel's target
+and options are fixed and every later Init() call returns -1. Create a new
+Channel to use a different target or configuration.
+
Init() can connect one server or a cluster(multiple servers).
# Connect to a server
diff --git a/src/brpc/channel.cpp b/src/brpc/channel.cpp
index 01d86375..ff81e521 100644
--- a/src/brpc/channel.cpp
+++ b/src/brpc/channel.cpp
@@ -254,6 +254,10 @@ int Channel::InitChannelOptions(const ChannelOptions*
options) {
int Channel::Init(const char* server_addr_and_port,
const ChannelOptions* options) {
+ if (_server_id != INVALID_SOCKET_ID || _lb != NULL) {
+ LOG(ERROR) << "Channel=" << this << " has already been initialized";
+ return -1;
+ }
GlobalInitializeOrDie();
butil::EndPoint point;
const AdaptiveProtocolType& ptype = (options ? options->protocol :
_options.protocol);
@@ -287,6 +291,10 @@ int Channel::Init(const char* server_addr_and_port,
int Channel::Init(const char* server_addr, int port,
const ChannelOptions* options) {
+ if (_server_id != INVALID_SOCKET_ID || _lb != NULL) {
+ LOG(ERROR) << "Channel=" << this << " has already been initialized";
+ return -1;
+ }
GlobalInitializeOrDie();
butil::EndPoint point;
const AdaptiveProtocolType& ptype = (options ? options->protocol :
_options.protocol);
@@ -358,6 +366,10 @@ int Channel::InitSingle(const butil::EndPoint&
server_addr_and_port,
const char* raw_server_address,
const ChannelOptions* options,
int raw_port) {
+ if (_server_id != INVALID_SOCKET_ID || _lb != NULL) {
+ LOG(ERROR) << "Channel=" << this << " has already been initialized";
+ return -1;
+ }
GlobalInitializeOrDie();
if (InitChannelOptions(options) != 0) {
return -1;
@@ -410,6 +422,10 @@ int Channel::Init(const char* ns_url,
// Treat ns_url as server_addr_and_port
return Init(ns_url, options);
}
+ if (_server_id != INVALID_SOCKET_ID || _lb != NULL) {
+ LOG(ERROR) << "Channel=" << this << " has already been initialized";
+ return -1;
+ }
GlobalInitializeOrDie();
if (InitChannelOptions(options) != 0) {
return -1;
diff --git a/src/brpc/channel.h b/src/brpc/channel.h
index d13ae52d..47f262f6 100644
--- a/src/brpc/channel.h
+++ b/src/brpc/channel.h
@@ -184,6 +184,9 @@ public:
DISALLOW_COPY_AND_ASSIGN(Channel);
+ // Init() may be retried after failure, but a successful initialization is
+ // final: subsequent calls return -1.
+
// Connect this channel to a single server whose address is given by the
// first parameter. Use default options if `options' is nullptr.
int Init(butil::EndPoint server_addr_and_port, const ChannelOptions*
options);
diff --git a/test/brpc_channel_unittest.cpp b/test/brpc_channel_unittest.cpp
index 997859ef..b6125e26 100644
--- a/test/brpc_channel_unittest.cpp
+++ b/test/brpc_channel_unittest.cpp
@@ -2316,6 +2316,55 @@ TEST_F(ChannelTest, init_as_single_server) {
}
}
+TEST_F(ChannelTest, reject_reinitialization_after_successful_init) {
+ butil::EndPoint first_endpoint;
+ butil::EndPoint second_endpoint;
+ ASSERT_EQ(0, str2endpoint("127.0.0.1:59347", &first_endpoint));
+ ASSERT_EQ(0, str2endpoint("127.0.0.1:59348", &second_endpoint));
+ const brpc::SocketMapKey first_key(first_endpoint);
+ const brpc::SocketMapKey second_key(second_endpoint);
+
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init(first_endpoint, NULL));
+ brpc::SocketId id;
+ ASSERT_EQ(0, brpc::SocketMapFind(first_key, &id));
+ ASSERT_EQ(channel._server_id, id);
+ ASSERT_EQ(-1, channel.Init(first_endpoint, NULL));
+ ASSERT_EQ(-1, channel.Init(second_endpoint, NULL));
+ ASSERT_EQ(-1, channel.Init("unknown://unknown", "rr", NULL));
+ }
+
+ brpc::SocketId id;
+ EXPECT_NE(0, brpc::SocketMapFind(first_key, &id));
+ EXPECT_NE(0, brpc::SocketMapFind(second_key, &id));
+}
+
+TEST_F(ChannelTest, retry_init_after_failed_init) {
+ butil::EndPoint endpoint;
+ ASSERT_EQ(0, str2endpoint("127.0.0.1:59349", &endpoint));
+ const brpc::SocketMapKey key(endpoint);
+
+ {
+ brpc::Channel channel;
+ brpc::ChannelOptions invalid_options;
+ invalid_options.client_host = "not a valid client host";
+ ASSERT_EQ(-1, channel.Init(endpoint, &invalid_options));
+ EXPECT_EQ(brpc::INVALID_SOCKET_ID, channel._server_id);
+
+ brpc::ChannelOptions valid_options;
+ ASSERT_EQ(0, channel.Init(endpoint, &valid_options));
+ EXPECT_NE(brpc::INVALID_SOCKET_ID, channel._server_id);
+ EXPECT_EQ(endpoint, channel._server_address);
+ brpc::SocketId id;
+ ASSERT_EQ(0, brpc::SocketMapFind(key, &id));
+ ASSERT_EQ(channel._server_id, id);
+ }
+
+ brpc::SocketId id;
+ EXPECT_NE(0, brpc::SocketMapFind(key, &id));
+}
+
TEST_F(ChannelTest, init_using_unknown_naming_service) {
brpc::Channel channel;
ASSERT_EQ(-1, channel.Init("unknown://unknown", "unknown", nullptr));
@@ -2391,73 +2440,138 @@ TEST_F(ChannelTest, parse_hostname) {
brpc::ChannelOptions opt;
opt.succeed_without_server = false;
opt.protocol = brpc::PROTOCOL_HTTP;
- brpc::Channel channel;
- ASSERT_EQ(-1, channel.Init("", 8888, &opt));
- ASSERT_EQ("", channel._service_name);
- ASSERT_EQ(-1, channel.Init("", &opt));
- ASSERT_EQ("", channel._service_name);
-
- ASSERT_EQ(0, channel.Init("http://127.0.0.1", 8888, &opt));
- ASSERT_EQ("127.0.0.1:8888", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://127.0.0.1:8888", &opt));
- ASSERT_EQ("127.0.0.1:8888", channel._service_name);
-
- ASSERT_EQ(0, channel.Init("localhost", 8888, &opt));
- ASSERT_EQ("localhost:8888", channel._service_name);
- ASSERT_EQ(0, channel.Init("localhost:8888", &opt));
- ASSERT_EQ("localhost:8888", channel._service_name);
-
- ASSERT_EQ(0, channel.Init("http://www.baidu.com", &opt));
- ASSERT_EQ("www.baidu.com", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com:80", &opt));
- ASSERT_EQ("www.baidu.com:80", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com", 80, &opt));
- ASSERT_EQ("www.baidu.com:80", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com:8888", &opt));
- ASSERT_EQ("www.baidu.com:8888", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com", 8888, &opt));
- ASSERT_EQ("www.baidu.com:8888", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com", "rr", &opt));
- ASSERT_EQ("www.baidu.com", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com:80", "rr", &opt));
- ASSERT_EQ("www.baidu.com:80", channel._service_name);
- ASSERT_EQ(0, channel.Init("http://www.baidu.com:8888", "rr", &opt));
- ASSERT_EQ("www.baidu.com:8888", channel._service_name);
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(-1, channel.Init("", 8888, &opt));
+ ASSERT_EQ("", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(-1, channel.Init("", &opt));
+ ASSERT_EQ("", channel._service_name);
+ }
+
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://127.0.0.1", 8888, &opt));
+ ASSERT_EQ("127.0.0.1:8888", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://127.0.0.1:8888", &opt));
+ ASSERT_EQ("127.0.0.1:8888", channel._service_name);
+ }
+
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("localhost", 8888, &opt));
+ ASSERT_EQ("localhost:8888", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("localhost:8888", &opt));
+ ASSERT_EQ("localhost:8888", channel._service_name);
+ }
+
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com", &opt));
+ ASSERT_EQ("www.baidu.com", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com:80", &opt));
+ ASSERT_EQ("www.baidu.com:80", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com", 80, &opt));
+ ASSERT_EQ("www.baidu.com:80", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com:8888", &opt));
+ ASSERT_EQ("www.baidu.com:8888", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com", 8888, &opt));
+ ASSERT_EQ("www.baidu.com:8888", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com", "rr", &opt));
+ ASSERT_EQ("www.baidu.com", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com:80", "rr", &opt));
+ ASSERT_EQ("www.baidu.com:80", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("http://www.baidu.com:8888", "rr", &opt));
+ ASSERT_EQ("www.baidu.com:8888", channel._service_name);
+ }
opt.mutable_ssl_options()->verify.verify_mode =
brpc::VerifyMode::VERIFY_PEER;
opt.mutable_ssl_options()->verify.verify_depth = 1;
opt.mutable_ssl_options()->verify.ca_file_path = "cert1.crt";
- ASSERT_EQ(0, channel.Init("https://www.baidu.com", &opt));
- ASSERT_EQ("www.baidu.com", channel._service_name);
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com", &opt));
+ ASSERT_EQ("www.baidu.com", channel._service_name);
#if defined(USE_MESALINK) || \
(!defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_NUMBER < 0x10002000L)
-
ASSERT_TRUE(channel._options.ssl_options().verify.expected_peer_name.empty());
+
ASSERT_TRUE(channel._options.ssl_options().verify.expected_peer_name.empty());
#else
- ASSERT_EQ("www.baidu.com",
- channel._options.ssl_options().verify.expected_peer_name);
+ ASSERT_EQ("www.baidu.com",
+ channel._options.ssl_options().verify.expected_peer_name);
#endif
- ASSERT_EQ(0, channel.Init("https://www.baidu.com:443", &opt));
- ASSERT_EQ("www.baidu.com:443", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com:443", &opt));
+ ASSERT_EQ("www.baidu.com:443", channel._service_name);
#if defined(USE_MESALINK) || \
(!defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_NUMBER < 0x10002000L)
-
ASSERT_TRUE(channel._options.ssl_options().verify.expected_peer_name.empty());
+
ASSERT_TRUE(channel._options.ssl_options().verify.expected_peer_name.empty());
#else
- ASSERT_EQ("www.baidu.com",
- channel._options.ssl_options().verify.expected_peer_name);
+ ASSERT_EQ("www.baidu.com",
+ channel._options.ssl_options().verify.expected_peer_name);
#endif
- ASSERT_EQ(0, channel.Init("https://www.baidu.com", 443, &opt));
- ASSERT_EQ("www.baidu.com:443", channel._service_name);
- ASSERT_EQ(0, channel.Init("https://www.baidu.com:1443", &opt));
- ASSERT_EQ("www.baidu.com:1443", channel._service_name);
- ASSERT_EQ(0, channel.Init("https://www.baidu.com", 1443, &opt));
- ASSERT_EQ("www.baidu.com:1443", channel._service_name);
- ASSERT_EQ(0, channel.Init("https://www.baidu.com", "rr", &opt));
- ASSERT_EQ("www.baidu.com", channel._service_name);
- ASSERT_EQ(0, channel.Init("https://www.baidu.com:443", "rr", &opt));
- ASSERT_EQ("www.baidu.com:443", channel._service_name);
- ASSERT_EQ(0, channel.Init("https://www.baidu.com:1443", "rr", &opt));
- ASSERT_EQ("www.baidu.com:1443", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com", 443, &opt));
+ ASSERT_EQ("www.baidu.com:443", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com:1443", &opt));
+ ASSERT_EQ("www.baidu.com:1443", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com", 1443, &opt));
+ ASSERT_EQ("www.baidu.com:1443", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com", "rr", &opt));
+ ASSERT_EQ("www.baidu.com", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com:443", "rr", &opt));
+ ASSERT_EQ("www.baidu.com:443", channel._service_name);
+ }
+ {
+ brpc::Channel channel;
+ ASSERT_EQ(0, channel.Init("https://www.baidu.com:1443", "rr", &opt));
+ ASSERT_EQ("www.baidu.com:1443", channel._service_name);
+ }
const char *address_list[] = {
"10.127.0.1:1234",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]