be/src/util/brpc_client_cache.h
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #pragma once |
19 | | |
20 | | #include <brpc/adaptive_connection_type.h> |
21 | | #include <brpc/adaptive_protocol_type.h> |
22 | | #include <brpc/channel.h> |
23 | | #include <brpc/controller.h> |
24 | | #include <butil/endpoint.h> |
25 | | #include <fmt/format.h> |
26 | | #include <gen_cpp/Types_types.h> |
27 | | #include <gen_cpp/types.pb.h> |
28 | | #include <glog/logging.h> |
29 | | #include <google/protobuf/service.h> |
30 | | #include <parallel_hashmap/phmap.h> |
31 | | #include <stddef.h> |
32 | | |
33 | | #include <functional> |
34 | | #include <memory> |
35 | | #include <mutex> |
36 | | #include <ostream> |
37 | | #include <string> |
38 | | #include <utility> |
39 | | #include <vector> |
40 | | |
41 | | #include "common/compiler_util.h" // IWYU pragma: keep |
42 | | #include "common/config.h" |
43 | | #include "common/status.h" |
44 | | #include "runtime/exec_env.h" |
45 | | #include "service/backend_options.h" |
46 | | #include "util/client_connection_provider.h" |
47 | | #include "util/defer_op.h" |
48 | | #include "util/dns_cache.h" |
49 | | #include "util/network_util.h" |
50 | | |
51 | | namespace doris { |
52 | | class PBackendService_Stub; |
53 | | class PFunctionService_Stub; |
54 | | } // namespace doris |
55 | | |
56 | | // Entry that holds both resolved IP and stub, similar to Java's BackendServiceClientExtIp |
57 | | template <typename T> |
58 | | struct StubEntry { |
59 | | std::string real_ip; |
60 | | std::shared_ptr<T> stub; |
61 | | }; |
62 | | |
63 | | template <typename T> |
64 | | using StubMap = phmap::parallel_flat_hash_map< |
65 | | std::string, StubEntry<T>, std::hash<std::string>, std::equal_to<std::string>, |
66 | | std::allocator<std::pair<const std::string, StubEntry<T>>>, 8, std::mutex>; |
67 | | |
68 | | namespace doris { |
69 | | class FailureDetectClosure : public ::google::protobuf::Closure { |
70 | | public: |
71 | | FailureDetectClosure(std::shared_ptr<AtomicStatus>& channel_st, |
72 | | ::google::protobuf::RpcController* controller, |
73 | | ::google::protobuf::Closure* done) |
74 | 2.29M | : _channel_st(channel_st), _controller(controller), _done(done) {} |
75 | | |
76 | 2.28M | void Run() override { |
77 | 2.28M | Defer defer {[&]() { delete this; }}; |
78 | | // All brpc related API will use brpc::Controller, so that it is safe |
79 | | // to do static cast here. |
80 | 2.28M | auto* cntl = static_cast<brpc::Controller*>(_controller); |
81 | 2.28M | if (cntl->Failed() && cntl->ErrorCode() == EHOSTDOWN) { |
82 | 1 | Status error_st = Status::NetworkError( |
83 | 1 | "Failed to send brpc, error={}, error_text={}, client: {}, latency = {}", |
84 | 1 | berror(cntl->ErrorCode()), cntl->ErrorText(), BackendOptions::get_localhost(), |
85 | 1 | cntl->latency_us()); |
86 | 1 | LOG(WARNING) << error_st; |
87 | 1 | _channel_st->update(error_st); |
88 | 1 | } |
89 | | // Sometimes done == nullptr, for example hand_shake API. |
90 | 2.29M | if (_done != nullptr) { |
91 | 2.29M | _done->Run(); |
92 | 2.29M | } |
93 | | // _done->Run may throw exception, so that move delete this to Defer. |
94 | | // delete this; |
95 | 2.28M | } |
96 | | |
97 | | private: |
98 | | std::shared_ptr<AtomicStatus> _channel_st; |
99 | | ::google::protobuf::RpcController* _controller; |
100 | | ::google::protobuf::Closure* _done; |
101 | | }; |
102 | | |
103 | | // This channel will use FailureDetectClosure to wrap the original closure |
104 | | // If some non-recoverable rpc failure happens, it will save the error status in |
105 | | // _channel_st. |
106 | | // And brpc client cache will depend on it to detect if the client is health. |
107 | | class FailureDetectChannel : public ::brpc::Channel { |
108 | | public: |
109 | 59 | FailureDetectChannel() : ::brpc::Channel() { |
110 | 59 | _channel_st = std::make_shared<AtomicStatus>(); // default OK |
111 | 59 | } |
112 | | void CallMethod(const google::protobuf::MethodDescriptor* method, |
113 | | google::protobuf::RpcController* controller, |
114 | | const google::protobuf::Message* request, google::protobuf::Message* response, |
115 | 2.29M | google::protobuf::Closure* done) override { |
116 | 2.29M | FailureDetectClosure* failure_detect_closure = nullptr; |
117 | 2.29M | if (done != nullptr) { |
118 | | // If done == nullptr, then it means the call is sync call, so that should not |
119 | | // gen a failure detect closure for it. Or it will core. |
120 | 2.28M | failure_detect_closure = new FailureDetectClosure(_channel_st, controller, done); |
121 | 2.28M | } |
122 | 2.29M | ::brpc::Channel::CallMethod(method, controller, request, response, failure_detect_closure); |
123 | | // Done == nullptr, it is a sync call, should also deal with the bad channel. |
124 | 2.29M | if (done == nullptr) { |
125 | 8.45k | auto* cntl = static_cast<brpc::Controller*>(controller); |
126 | 8.45k | if (cntl->Failed() && cntl->ErrorCode() == EHOSTDOWN) { |
127 | 2 | Status error_st = Status::NetworkError( |
128 | 2 | "Failed to send brpc, error={}, error_text={}, client: {}, latency = {}", |
129 | 2 | berror(cntl->ErrorCode()), cntl->ErrorText(), |
130 | 2 | BackendOptions::get_localhost(), cntl->latency_us()); |
131 | 2 | LOG(WARNING) << error_st; |
132 | 2 | _channel_st->update(error_st); |
133 | 2 | } |
134 | 8.45k | } |
135 | 2.29M | } |
136 | | |
137 | 2.51M | std::shared_ptr<AtomicStatus> channel_status() { return _channel_st; } |
138 | | |
139 | | private: |
140 | | std::shared_ptr<AtomicStatus> _channel_st; |
141 | | }; |
142 | | |
143 | | template <class T> |
144 | | class BrpcClientCache { |
145 | | public: |
146 | | BrpcClientCache(std::string protocol = "baidu_std", std::string connection_type = "", |
147 | | std::string connection_group = ""); |
148 | | virtual ~BrpcClientCache(); |
149 | | |
150 | | std::shared_ptr<T> get_client(const butil::EndPoint& endpoint) { |
151 | | return get_client(butil::endpoint2str(endpoint).c_str()); |
152 | | } |
153 | | |
154 | | #ifdef BE_TEST |
155 | | virtual std::shared_ptr<T> get_client(const TNetworkAddress& taddr) { |
156 | | std::string host_port = fmt::format("{}:{}", taddr.hostname, taddr.port); |
157 | | return get_client(host_port); |
158 | | } |
159 | | #else |
160 | 20.0k | std::shared_ptr<T> get_client(const TNetworkAddress& taddr) { |
161 | 20.0k | return get_client(taddr.hostname, taddr.port); |
162 | 20.0k | } |
163 | | #endif |
164 | | |
165 | 51 | std::shared_ptr<T> get_client(const PNetworkAddress& paddr) { |
166 | 51 | return get_client(paddr.hostname(), paddr.port()); |
167 | 51 | } |
168 | | |
169 | 2.49M | std::shared_ptr<T> get_client(const std::string& host, int port) { |
170 | 2.49M | std::string realhost = host; |
171 | 2.49M | auto dns_cache = ExecEnv::GetInstance()->dns_cache(); |
172 | 2.49M | if (dns_cache == nullptr) { |
173 | 8 | LOG(WARNING) << "DNS cache is not initialized, skipping hostname resolve"; |
174 | 2.49M | } else if (!is_valid_ip(host)) { |
175 | 0 | Status status = dns_cache->get(host, &realhost); |
176 | 0 | if (!status.ok()) { |
177 | 0 | LOG(WARNING) << "failed to get ip from host:" << status.to_string(); |
178 | | // The hostname is no longer resolvable, which normally means the backend |
179 | | // was dropped from the cluster. Returning early is not enough: any stub |
180 | | // cached under this host:port still holds a brpc Channel bound to the last |
181 | | // resolved (now dead) IP, and brpc keeps health-checking that socket |
182 | | // forever, which is the source of the endless |
183 | | // "Fail to wait EPOLLOUT ... Connection timed out" warnings. Drop it here |
184 | | // so the socket is closed along with the last reference to the stub. |
185 | 0 | _stub_map.erase(fmt::format("{}:{}", host, port)); |
186 | 0 | return nullptr; |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | // Use original host:port as key (like Java's TNetworkAddress address) |
191 | | // This allows us to detect IP changes when DNS resolution changes |
192 | 2.49M | std::string host_port = fmt::format("{}:{}", host, port); |
193 | | |
194 | 2.49M | std::shared_ptr<T> stub_ptr; |
195 | 2.49M | bool need_remove = false; |
196 | | |
197 | 2.51M | auto check_entry = [&](const auto& v) { |
198 | 2.51M | const StubEntry<T>& entry = v.second; |
199 | | // Check if cached IP matches current resolved IP |
200 | 2.51M | if (entry.real_ip != realhost) { |
201 | | // IP changed (DNS resolution changed) |
202 | 0 | LOG(WARNING) << "Cached ip changed for " << host << ", before ip: " << entry.real_ip |
203 | 0 | << ", current ip: " << realhost; |
204 | 0 | need_remove = true; |
205 | 2.51M | } else if (!static_cast<FailureDetectChannel*>(entry.stub->channel()) |
206 | 2.51M | ->channel_status() |
207 | 2.51M | ->ok()) { |
208 | | // Client is not in normal state, need to recreate |
209 | | // At this point we cannot judge the progress of reconnecting the underlying channel. |
210 | | // In the worst case, it may take two minutes. But we can't stand the connection refused |
211 | | // for two minutes, so rebuild the channel directly. |
212 | 2 | need_remove = true; |
213 | 2.51M | } else { |
214 | | // Cache hit: IP matches and client is healthy |
215 | 2.51M | stub_ptr = entry.stub; |
216 | 2.51M | } |
217 | 2.51M | }; Unexecuted instantiation: _ZZN5doris15BrpcClientCacheINS_21PFunctionService_StubEE10get_clientERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiENKUlRKT_E_clISt4pairIS9_9StubEntryIS1_EEEEDaSD_ _ZZN5doris15BrpcClientCacheINS_20PBackendService_StubEE10get_clientERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiENKUlRKT_E_clISt4pairIS9_9StubEntryIS1_EEEEDaSD_ Line | Count | Source | 197 | 2.51M | auto check_entry = [&](const auto& v) { | 198 | 2.51M | const StubEntry<T>& entry = v.second; | 199 | | // Check if cached IP matches current resolved IP | 200 | 2.51M | if (entry.real_ip != realhost) { | 201 | | // IP changed (DNS resolution changed) | 202 | 0 | LOG(WARNING) << "Cached ip changed for " << host << ", before ip: " << entry.real_ip | 203 | 0 | << ", current ip: " << realhost; | 204 | 0 | need_remove = true; | 205 | 2.51M | } else if (!static_cast<FailureDetectChannel*>(entry.stub->channel()) | 206 | 2.51M | ->channel_status() | 207 | 2.51M | ->ok()) { | 208 | | // Client is not in normal state, need to recreate | 209 | | // At this point we cannot judge the progress of reconnecting the underlying channel. | 210 | | // In the worst case, it may take two minutes. But we can't stand the connection refused | 211 | | // for two minutes, so rebuild the channel directly. | 212 | 2 | need_remove = true; | 213 | 2.51M | } else { | 214 | | // Cache hit: IP matches and client is healthy | 215 | 2.51M | stub_ptr = entry.stub; | 216 | 2.51M | } | 217 | 2.51M | }; |
|
218 | | |
219 | 2.50M | if (LIKELY(_stub_map.if_contains(host_port, check_entry))) { |
220 | 2.50M | if (stub_ptr != nullptr) { |
221 | 2.50M | return stub_ptr; |
222 | 2.50M | } |
223 | | // IP changed or client unhealthy, need to remove old entry |
224 | 18.4E | if (need_remove) { |
225 | 2 | _stub_map.erase(host_port); |
226 | 2 | } |
227 | 18.4E | } |
228 | | |
229 | | // Create new stub using resolved IP for actual connection |
230 | 18.4E | std::string real_host_port = get_host_port(realhost, port); |
231 | 18.4E | auto stub = get_new_client_no_cache(real_host_port); |
232 | 18.4E | if (stub != nullptr) { |
233 | 19 | StubEntry<T> entry {realhost, stub}; |
234 | 19 | _stub_map.try_emplace_l( |
235 | 19 | host_port, [&stub](const auto& v) { stub = v.second.stub; }, entry);Unexecuted instantiation: _ZZN5doris15BrpcClientCacheINS_21PFunctionService_StubEE10get_clientERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiENKUlRKT_E0_clISt4pairIS9_9StubEntryIS1_EEEEDaSD_ Unexecuted instantiation: _ZZN5doris15BrpcClientCacheINS_20PBackendService_StubEE10get_clientERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiENKUlRKT_E0_clISt4pairIS9_9StubEntryIS1_EEEEDaSD_ |
236 | 19 | } |
237 | 18.4E | return stub; |
238 | 2.49M | } Unexecuted instantiation: _ZN5doris15BrpcClientCacheINS_21PFunctionService_StubEE10get_clientERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEi _ZN5doris15BrpcClientCacheINS_20PBackendService_StubEE10get_clientERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEi Line | Count | Source | 169 | 2.49M | std::shared_ptr<T> get_client(const std::string& host, int port) { | 170 | 2.49M | std::string realhost = host; | 171 | 2.49M | auto dns_cache = ExecEnv::GetInstance()->dns_cache(); | 172 | 2.49M | if (dns_cache == nullptr) { | 173 | 8 | LOG(WARNING) << "DNS cache is not initialized, skipping hostname resolve"; | 174 | 2.49M | } else if (!is_valid_ip(host)) { | 175 | 0 | Status status = dns_cache->get(host, &realhost); | 176 | 0 | if (!status.ok()) { | 177 | 0 | LOG(WARNING) << "failed to get ip from host:" << status.to_string(); | 178 | | // The hostname is no longer resolvable, which normally means the backend | 179 | | // was dropped from the cluster. Returning early is not enough: any stub | 180 | | // cached under this host:port still holds a brpc Channel bound to the last | 181 | | // resolved (now dead) IP, and brpc keeps health-checking that socket | 182 | | // forever, which is the source of the endless | 183 | | // "Fail to wait EPOLLOUT ... Connection timed out" warnings. Drop it here | 184 | | // so the socket is closed along with the last reference to the stub. | 185 | 0 | _stub_map.erase(fmt::format("{}:{}", host, port)); | 186 | 0 | return nullptr; | 187 | 0 | } | 188 | 0 | } | 189 | | | 190 | | // Use original host:port as key (like Java's TNetworkAddress address) | 191 | | // This allows us to detect IP changes when DNS resolution changes | 192 | 2.49M | std::string host_port = fmt::format("{}:{}", host, port); | 193 | | | 194 | 2.49M | std::shared_ptr<T> stub_ptr; | 195 | 2.49M | bool need_remove = false; | 196 | | | 197 | 2.49M | auto check_entry = [&](const auto& v) { | 198 | 2.49M | const StubEntry<T>& entry = v.second; | 199 | | // Check if cached IP matches current resolved IP | 200 | 2.49M | if (entry.real_ip != realhost) { | 201 | | // IP changed (DNS resolution changed) | 202 | 2.49M | LOG(WARNING) << "Cached ip changed for " << host << ", before ip: " << entry.real_ip | 203 | 2.49M | << ", current ip: " << realhost; | 204 | 2.49M | need_remove = true; | 205 | 2.49M | } else if (!static_cast<FailureDetectChannel*>(entry.stub->channel()) | 206 | 2.49M | ->channel_status() | 207 | 2.49M | ->ok()) { | 208 | | // Client is not in normal state, need to recreate | 209 | | // At this point we cannot judge the progress of reconnecting the underlying channel. | 210 | | // In the worst case, it may take two minutes. But we can't stand the connection refused | 211 | | // for two minutes, so rebuild the channel directly. | 212 | 2.49M | need_remove = true; | 213 | 2.49M | } else { | 214 | | // Cache hit: IP matches and client is healthy | 215 | 2.49M | stub_ptr = entry.stub; | 216 | 2.49M | } | 217 | 2.49M | }; | 218 | | | 219 | 2.50M | if (LIKELY(_stub_map.if_contains(host_port, check_entry))) { | 220 | 2.50M | if (stub_ptr != nullptr) { | 221 | 2.50M | return stub_ptr; | 222 | 2.50M | } | 223 | | // IP changed or client unhealthy, need to remove old entry | 224 | 18.4E | if (need_remove) { | 225 | 2 | _stub_map.erase(host_port); | 226 | 2 | } | 227 | 18.4E | } | 228 | | | 229 | | // Create new stub using resolved IP for actual connection | 230 | 18.4E | std::string real_host_port = get_host_port(realhost, port); | 231 | 18.4E | auto stub = get_new_client_no_cache(real_host_port); | 232 | 18.4E | if (stub != nullptr) { | 233 | 19 | StubEntry<T> entry {realhost, stub}; | 234 | 19 | _stub_map.try_emplace_l( | 235 | 19 | host_port, [&stub](const auto& v) { stub = v.second.stub; }, entry); | 236 | 19 | } | 237 | 18.4E | return stub; | 238 | 2.49M | } |
|
239 | | |
240 | 0 | std::shared_ptr<T> get_client(const std::string& host_port) { |
241 | 0 | const auto pos = host_port.rfind(':'); |
242 | 0 | std::string host = host_port.substr(0, pos); |
243 | 0 | int port = 0; |
244 | 0 | try { |
245 | 0 | port = stoi(host_port.substr(pos + 1)); |
246 | 0 | } catch (const std::exception& err) { |
247 | 0 | LOG(WARNING) << "failed to parse port from " << host_port << ": " << err.what(); |
248 | 0 | return nullptr; |
249 | 0 | } |
250 | 0 | return get_client(host, port); |
251 | 0 | } |
252 | | |
253 | | std::shared_ptr<T> get_new_client_no_cache(const std::string& host_port, |
254 | | const std::string& protocol = "", |
255 | | const std::string& connection_type = "", |
256 | 59 | const std::string& connection_group = "") { |
257 | 59 | brpc::ChannelOptions options; |
258 | 59 | Status status = doris::client::configure_brpc_channel_options(&options); |
259 | 59 | if (!status.ok()) { |
260 | 0 | throw status; |
261 | 0 | } |
262 | 59 | if (protocol != "") { |
263 | 0 | options.protocol = protocol; |
264 | 59 | } else if (_protocol != "") { |
265 | 59 | options.protocol = _protocol; |
266 | 59 | } |
267 | 59 | if (connection_type != "") { |
268 | 0 | options.connection_type = connection_type; |
269 | 59 | } else if (_connection_type != "") { |
270 | 4 | options.connection_type = _connection_type; |
271 | 4 | } |
272 | 59 | if (connection_group != "") { |
273 | 0 | options.connection_group = connection_group; |
274 | 59 | } else if (_connection_group != "") { |
275 | 4 | options.connection_group = _connection_group; |
276 | 4 | } |
277 | | // Add random connection id to connection_group to make sure use new socket |
278 | 59 | options.connection_group += std::to_string(_connection_id.fetch_add(1)); |
279 | 59 | options.connect_timeout_ms = 2000; |
280 | 59 | options.timeout_ms = 2000; |
281 | 59 | options.max_retry = 10; |
282 | | |
283 | 59 | std::unique_ptr<FailureDetectChannel> channel(new FailureDetectChannel()); |
284 | 59 | int ret_code = 0; |
285 | 59 | if (host_port.find("://") == std::string::npos) { |
286 | 59 | ret_code = channel->Init(host_port.c_str(), &options); |
287 | 59 | } else { |
288 | 0 | ret_code = |
289 | 0 | channel->Init(host_port.c_str(), config::rpc_load_balancer.c_str(), &options); |
290 | 0 | } |
291 | 59 | if (ret_code) { |
292 | 1 | LOG(WARNING) << "Failed to initialize brpc Channel to " << host_port; |
293 | 1 | return nullptr; |
294 | 1 | } |
295 | 58 | return std::make_shared<T>(channel.release(), google::protobuf::Service::STUB_OWNS_CHANNEL); |
296 | 59 | } _ZN5doris15BrpcClientCacheINS_20PBackendService_StubEE23get_new_client_no_cacheERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESA_SA_SA_ Line | Count | Source | 256 | 59 | const std::string& connection_group = "") { | 257 | 59 | brpc::ChannelOptions options; | 258 | 59 | Status status = doris::client::configure_brpc_channel_options(&options); | 259 | 59 | if (!status.ok()) { | 260 | 0 | throw status; | 261 | 0 | } | 262 | 59 | if (protocol != "") { | 263 | 0 | options.protocol = protocol; | 264 | 59 | } else if (_protocol != "") { | 265 | 59 | options.protocol = _protocol; | 266 | 59 | } | 267 | 59 | if (connection_type != "") { | 268 | 0 | options.connection_type = connection_type; | 269 | 59 | } else if (_connection_type != "") { | 270 | 4 | options.connection_type = _connection_type; | 271 | 4 | } | 272 | 59 | if (connection_group != "") { | 273 | 0 | options.connection_group = connection_group; | 274 | 59 | } else if (_connection_group != "") { | 275 | 4 | options.connection_group = _connection_group; | 276 | 4 | } | 277 | | // Add random connection id to connection_group to make sure use new socket | 278 | 59 | options.connection_group += std::to_string(_connection_id.fetch_add(1)); | 279 | 59 | options.connect_timeout_ms = 2000; | 280 | 59 | options.timeout_ms = 2000; | 281 | 59 | options.max_retry = 10; | 282 | | | 283 | 59 | std::unique_ptr<FailureDetectChannel> channel(new FailureDetectChannel()); | 284 | 59 | int ret_code = 0; | 285 | 59 | if (host_port.find("://") == std::string::npos) { | 286 | 59 | ret_code = channel->Init(host_port.c_str(), &options); | 287 | 59 | } else { | 288 | 0 | ret_code = | 289 | 0 | channel->Init(host_port.c_str(), config::rpc_load_balancer.c_str(), &options); | 290 | 0 | } | 291 | 59 | if (ret_code) { | 292 | 1 | LOG(WARNING) << "Failed to initialize brpc Channel to " << host_port; | 293 | 1 | return nullptr; | 294 | 1 | } | 295 | 58 | return std::make_shared<T>(channel.release(), google::protobuf::Service::STUB_OWNS_CHANNEL); | 296 | 59 | } |
Unexecuted instantiation: _ZN5doris15BrpcClientCacheINS_21PFunctionService_StubEE23get_new_client_no_cacheERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESA_SA_SA_ |
297 | | |
298 | 1 | size_t size() { return _stub_map.size(); } |
299 | | |
300 | 0 | void clear() { _stub_map.clear(); } |
301 | | |
302 | 0 | size_t erase(const std::string& host_port) { return _stub_map.erase(host_port); } |
303 | | |
304 | 0 | size_t erase(const std::string& host, int port) { |
305 | 0 | std::string host_port = fmt::format("{}:{}", host, port); |
306 | 0 | return erase(host_port); |
307 | 0 | } |
308 | | |
309 | 0 | size_t erase(const butil::EndPoint& endpoint) { |
310 | 0 | return _stub_map.erase(butil::endpoint2str(endpoint).c_str()); |
311 | 0 | } |
312 | | |
313 | 0 | bool exist(const std::string& host_port) { |
314 | 0 | return _stub_map.find(host_port) != _stub_map.end(); |
315 | 0 | } |
316 | | |
317 | 0 | void get_all(std::vector<std::string>* endpoints) { |
318 | 0 | for (auto it = _stub_map.begin(); it != _stub_map.end(); ++it) { |
319 | 0 | endpoints->emplace_back(it->first.c_str()); |
320 | 0 | } |
321 | 0 | } |
322 | | |
323 | | bool available(std::shared_ptr<T> stub, const butil::EndPoint& endpoint) { |
324 | | return available(stub, butil::endpoint2str(endpoint).c_str()); |
325 | | } |
326 | | |
327 | 1 | bool available(std::shared_ptr<T> stub, const std::string& host_port) { |
328 | 1 | if (!stub) { |
329 | 0 | LOG(WARNING) << "stub is null to: " << host_port; |
330 | 0 | return false; |
331 | 0 | } |
332 | 1 | std::string message = "hello doris!"; |
333 | 1 | PHandShakeRequest request; |
334 | 1 | request.set_hello(message); |
335 | 1 | PHandShakeResponse response; |
336 | 1 | brpc::Controller cntl; |
337 | 1 | stub->hand_shake(&cntl, &request, &response, nullptr); |
338 | 1 | if (cntl.Failed()) { |
339 | 1 | LOG(WARNING) << "open brpc connection to " << host_port |
340 | 1 | << " failed: " << cntl.ErrorText(); |
341 | 1 | return false; |
342 | 1 | } else if (response.has_status() && response.has_hello() && response.hello() == message && |
343 | 0 | response.status().status_code() == 0) { |
344 | 0 | return true; |
345 | 0 | } else { |
346 | 0 | LOG(WARNING) << "open brpc connection to " << host_port |
347 | 0 | << " failed: " << response.DebugString(); |
348 | 0 | return false; |
349 | 0 | } |
350 | 1 | } |
351 | | |
352 | 1 | bool available(std::shared_ptr<T> stub, const std::string& host, int port) { |
353 | 1 | std::string host_port = fmt::format("{}:{}", host, port); |
354 | 1 | return available(stub, host_port); |
355 | 1 | } |
356 | | |
357 | | private: |
358 | | StubMap<T> _stub_map; |
359 | | const std::string _protocol; |
360 | | const std::string _connection_type; |
361 | | const std::string _connection_group; |
362 | | // use to generate unique connection id for each connection |
363 | | // to prevent the connection problem of brpc: https://github.com/apache/brpc/issues/2146 |
364 | | std::atomic<int64_t> _connection_id {0}; |
365 | | }; |
366 | | |
367 | | using InternalServiceClientCache = BrpcClientCache<PBackendService_Stub>; |
368 | | using FunctionServiceClientCache = BrpcClientCache<PFunctionService_Stub>; |
369 | | } // namespace doris |