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