VLink  2.0.0
A high-performance communication middleware
client-inl.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include <memory>
27 #include <string>
28 #include <utility>
29 
30 #include "../base/cpu_profiler_guard.h"
31 #include "../base/logger.h"
32 #include "../base/memory_resource.h"
33 #include "../client.h"
34 #include "../impl/url.h"
35 #include "../serializer.h"
36 
37 namespace vlink {
38 
39 template <typename ReqT, typename RespT, SecurityType SecT>
41  const std::string& url_str, InitType type) {
42  return std::make_unique<Client<ReqT, RespT, SecT>>(url_str, type);
43 }
44 
45 template <typename ReqT, typename RespT, SecurityType SecT>
47  const std::string& url_str, InitType type) {
48  return std::make_shared<Client<ReqT, RespT, SecT>>(url_str, type);
49 }
50 
51 template <typename ReqT, typename RespT, SecurityType SecT>
52 template <typename ConfT, typename>
53 inline Client<ReqT, RespT, SecT>::Client(const ConfT& conf, InitType type) {
54  static_assert(ConfT::get_allow_impl_type() & kImplType, "Conf does not support client mode.");
55 
56  if VUNLIKELY (!conf.parse(kImplType) || !conf.is_valid()) {
57  VLOG_F(conf, " client configuration is invalid or could not be parsed.");
58  return;
59  }
60 
61  this->impl_ = conf.create_client();
62 
63  if VUNLIKELY (!this->impl_) {
64  VLOG_F(conf, " client implementation not available for this transport.");
65  return;
66  }
67 
68  this->impl_->transport_type = conf.get_transport_type();
69  this->impl_->ser_type = Serializer::get_serialized_type<kReqType, ReqT>();
70 
71  if constexpr (kHasResp) {
72  const auto resp_ser_type = Serializer::get_serialized_type<kRespType, RespT>();
73 
74  if (!this->impl_->ser_type.empty() || !resp_ser_type.empty()) {
75  this->impl_->ser_type += "|" + resp_ser_type;
76  }
77  }
78 
79  {
80  constexpr auto kReqSchemaType = Serializer::get_schema_type<kReqType, ReqT>();
81  constexpr auto kRespSchemaType = Serializer::get_schema_type<kRespType, RespT>();
82 
83  if constexpr (kHasResp && kReqSchemaType != kRespSchemaType) {
84  this->impl_->schema_type = SchemaType::kUnknown;
85  } else {
86  this->impl_->schema_type = kReqSchemaType;
87  }
88  }
89 
90  this->impl_->is_cdr_type = Serializer::is_cdr_type<ReqT>();
91  this->impl_->is_resp_type = kHasResp;
92 
93  if constexpr (std::is_same_v<ConfT, Url>) {
94  this->impl_->url = conf.get_str();
95  }
96 
97  if constexpr (SecT == SecurityType::kWithSecurity) {
98  this->impl_->is_security_type = true;
99  }
100 
101  if VLIKELY (type == InitType::kWithInit) {
102  this->init(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
103  }
104 }
105 
106 template <typename ReqT, typename RespT, SecurityType SecT>
107 inline Client<ReqT, RespT, SecT>::Client(const std::string& url_str, InitType type)
108  : Client<ReqT, RespT, SecT>(Url(url_str), type) {}
109 
110 template <typename ReqT, typename RespT, SecurityType SecT>
112  {
113  std::lock_guard lock(future_mtx_);
114  future_map_.clear();
115  }
116 
117  // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
118  this->deinit();
119 }
120 
121 template <typename ReqT, typename RespT, SecurityType SecT>
123  this->impl_->detect_connected(std::move(callback));
124 }
125 
126 template <typename ReqT, typename RespT, SecurityType SecT>
127 inline bool Client<ReqT, RespT, SecT>::wait_for_connected(std::chrono::milliseconds timeout) {
128  if VUNLIKELY (timeout.count() == 0) {
129  VLOG_W("Client: Timeout value is 0, using infinite wait instead.");
130  timeout = Timeout::kInfinite;
131  }
132 
133  return this->impl_->wait_for_connected(timeout);
134 }
135 
136 template <typename ReqT, typename RespT, SecurityType SecT>
138  return this->impl_->is_connected();
139 }
140 
141 template <typename ReqT, typename RespT, SecurityType SecT>
142 inline bool Client<ReqT, RespT, SecT>::invoke(const ReqT& req, RespT& resp, std::chrono::milliseconds timeout) {
143  if VUNLIKELY (timeout.count() == 0) {
144  VLOG_W("Client: Timeout value is 0, using infinite wait instead.");
145  timeout = Timeout::kInfinite;
146  }
147 
148 #ifndef VLINK_DISABLE_PROFILER
149  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
150 #endif
151 
152  static_assert(kHasResp, "Invoke requires a response type.");
153 
154  bool ret = false;
155 
156  if constexpr (std::is_same_v<ReqT, Bytes> && std::is_same_v<RespT, Bytes>) {
157  ret = call_bytes(req, [&resp](const Bytes& resp_data) { resp = resp_data; }, timeout);
158  } else {
159  Bytes req_data;
160 
161  if constexpr (SecT != SecurityType::kWithSecurity) {
162  if (this->is_support_loan_) {
163  size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
164 
165  req_data = this->impl_->loan(ser_size);
166 
167  if VUNLIKELY (ser_size != 0 && req_data.empty()) {
168  return false;
169  }
170  }
171  }
172 
173  if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
174  VLOG_T("Client serialize failed, url: ", this->impl_->url, ".");
175 
176  if constexpr (SecT != SecurityType::kWithSecurity) {
177  if (this->is_support_loan_) {
178  this->impl_->return_loan(req_data);
179  }
180  }
181 
182  return false;
183  }
184 
185  bool deserialize_success = false;
186 
187  ret = call_bytes(
188  req_data,
189  [this, &resp, &deserialize_success](const Bytes& resp_data) {
190  if VLIKELY (Serializer::deserialize<kRespType>(resp_data, resp, this->impl_->transport_type)) {
191  deserialize_success = true;
192  } else {
193  VLOG_T("Client deserialize failed, url: ", this->impl_->url, ".");
194  }
195  },
196  timeout);
197 
198  ret = ret && deserialize_success;
199  }
200 
201  return ret;
202 }
203 
204 template <typename ReqT, typename RespT, SecurityType SecT>
205 inline std::optional<RespT> Client<ReqT, RespT, SecT>::invoke(const ReqT& req, std::chrono::milliseconds timeout) {
206  if VUNLIKELY (timeout.count() == 0) {
207  VLOG_W("Client: Timeout value is 0, using infinite wait instead.");
208  timeout = Timeout::kInfinite;
209  }
210 
211 #ifndef VLINK_DISABLE_PROFILER
212  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
213 #endif
214 
215  thread_local auto resp = this->template get_default_value<RespT>();
216 
217  if VLIKELY (invoke(req, resp, timeout)) {
218  return std::make_optional<RespT>(resp);
219  }
220 
221  return std::nullopt;
222 }
223 
224 template <typename ReqT, typename RespT, SecurityType SecT>
225 inline bool Client<ReqT, RespT, SecT>::invoke(const ReqT& req, RespCallback&& callback) {
226 #ifndef VLINK_DISABLE_PROFILER
227  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
228 #endif
229 
230  static_assert(kHasResp, "Invoke requires a response type.");
231 
232  bool ret = false;
233 
234  if constexpr (std::is_same_v<ReqT, Bytes> && std::is_same_v<RespT, Bytes>) {
235  ret = call_bytes(req, [callback = std::move(callback)](const Bytes& resp_data) { callback(resp_data); });
236  } else {
237  Bytes req_data;
238 
239  if constexpr (SecT != SecurityType::kWithSecurity) {
240  if (this->is_support_loan_) {
241  size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
242 
243  req_data = this->impl_->loan(ser_size);
244 
245  if VUNLIKELY (ser_size != 0 && req_data.empty()) {
246  return false;
247  }
248  }
249  }
250 
251  if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
252  VLOG_T("Client serialize failed, url: ", this->impl_->url, ".");
253 
254  if constexpr (SecT != SecurityType::kWithSecurity) {
255  if (this->is_support_loan_) {
256  this->impl_->return_loan(req_data);
257  }
258  }
259 
260  return false;
261  }
262 
263  ret = call_bytes(req_data, [this, callback = std::move(callback)](const Bytes& resp_data) {
264  thread_local auto resp = this->template get_default_value<RespT>();
265 
266  if VUNLIKELY (!Serializer::deserialize<kRespType>(resp_data, resp, this->impl_->transport_type)) {
267  VLOG_T("Client deserialize failed, url: ", this->impl_->url, ".");
268  return;
269  }
270 
271  callback(resp);
272  });
273  }
274 
275  return ret;
276 }
277 
278 template <typename ReqT, typename RespT, SecurityType SecT>
279 inline std::future<RespT> Client<ReqT, RespT, SecT>::async_invoke(const ReqT& req) {
280 #ifndef VLINK_DISABLE_PROFILER
281  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
282 #endif
283 
284  static_assert(kHasResp, "async_invoke requires a response type.");
285 
286  auto pro = MemoryResource::make_shared<std::promise<RespT>>();
287  auto future = pro->get_future();
288 
289  bool ret = false;
290  int64_t target_seq = 0;
291 
292  {
293  std::lock_guard lock(future_mtx_);
294  target_seq = future_seq_++;
295  future_map_.emplace(target_seq, pro);
296  }
297 
298  auto cleanup_on_error = [this, target_seq, pro](const std::string& error_str) {
299  std::lock_guard lock(future_mtx_);
300  future_map_.erase(target_seq);
301 
302  try {
303  throw Exception::RuntimeError(error_str);
304  } catch (std::exception&) {
305  pro->set_exception(std::current_exception());
306  }
307  };
308 
309  if constexpr (std::is_same_v<ReqT, Bytes> && std::is_same_v<RespT, Bytes>) {
310  ret = call_bytes(req, [this, target_seq](const Bytes& resp_data) {
311  std::lock_guard lock(future_mtx_);
312  auto it = future_map_.find(target_seq);
313 
314  if VLIKELY (it != future_map_.end()) {
315  it->second->set_value(resp_data);
316  future_map_.erase(it);
317  }
318  });
319  } else {
320  Bytes req_data;
321 
322  if constexpr (SecT != SecurityType::kWithSecurity) {
323  if (this->is_support_loan_) {
324  size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
325  req_data = this->impl_->loan(ser_size);
326 
327  if VUNLIKELY (ser_size != 0 && req_data.empty()) {
328  cleanup_on_error("Client async_invoke error (Failed to Loan)");
329  return future;
330  }
331  }
332  }
333 
334  if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
335  VLOG_T("Client serialize failed, url: ", this->impl_->url, ".");
336 
337  if constexpr (SecT != SecurityType::kWithSecurity) {
338  if (this->is_support_loan_) {
339  this->impl_->return_loan(req_data);
340  }
341  }
342 
343  cleanup_on_error("Client async_invoke error (Failed to serialize req)");
344  return future;
345  }
346 
347  ret = call_bytes(req_data, [this, target_seq](const Bytes& resp_data) {
348  bool convert_success = false;
349 
350  thread_local auto resp = this->template get_default_value<RespT>();
351 
352  if VLIKELY (Serializer::deserialize<kRespType>(resp_data, resp, this->impl_->transport_type)) {
353  convert_success = true;
354  } else {
355  VLOG_T("Client deserialize failed, url: ", this->impl_->url, ".");
356  }
357 
358  std::lock_guard lock(future_mtx_);
359 
360  auto it = future_map_.find(target_seq);
361 
362  if VLIKELY (it != future_map_.end()) {
363  if VLIKELY (convert_success) {
364  it->second->set_value(resp);
365  } else {
366  try {
367  throw Exception::RuntimeError("Client async_invoke error (Failed to deserialize resp)");
368  } catch (std::exception&) {
369  it->second->set_exception(std::current_exception());
370  }
371  }
372 
373  future_map_.erase(it);
374  }
375  });
376  }
377 
378  if VUNLIKELY (!ret) {
379  cleanup_on_error("Client async_invoke error (Failed to call)");
380  }
381 
382  return future;
383 }
384 
385 template <typename ReqT, typename RespT, SecurityType SecT>
386 inline bool Client<ReqT, RespT, SecT>::send(const ReqT& req) {
387 #ifndef VLINK_DISABLE_PROFILER
388  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
389 #endif
390 
391  static_assert(!kHasResp, "Send not supported; use invoke() for request-response.");
392 
393  bool ret = false;
394 
395  if constexpr (std::is_same_v<ReqT, Bytes>) {
396  ret = call_bytes(req);
397  } else {
398  Bytes req_data;
399 
400  if constexpr (SecT != SecurityType::kWithSecurity) {
401  if (this->is_support_loan_) {
402  size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
403 
404  req_data = this->impl_->loan(ser_size);
405 
406  if VUNLIKELY (ser_size != 0 && req_data.empty()) {
407  return false;
408  }
409  }
410  }
411 
412  if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
413  VLOG_T("Client serialize failed, url: ", this->impl_->url, ".");
414 
415  if constexpr (SecT != SecurityType::kWithSecurity) {
416  if (this->is_support_loan_) {
417  this->impl_->return_loan(req_data);
418  }
419  }
420 
421  return false;
422  }
423 
424  ret = call_bytes(req_data);
425  }
426 
427  return ret;
428 }
429 
430 template <typename ReqT, typename RespT, SecurityType SecT>
431 inline bool Client<ReqT, RespT, SecT>::call_bytes(const Bytes& req_data, NodeImpl::MsgCallback&& callback,
432  std::chrono::milliseconds timeout) {
433  if constexpr (SecT == SecurityType::kWithSecurity) {
434  Bytes req_sec_data;
435 
436  if VUNLIKELY (!this->impl_->security || !this->impl_->security->encrypt(req_data, req_sec_data)) {
437  VLOG_T("Client encrypt failed, url: ", this->impl_->url, ".");
438  return false;
439  }
440 
441  if VUNLIKELY (!callback) {
442  return this->impl_->call(req_sec_data, nullptr, timeout);
443  }
444 
445  return this->impl_->call(
446  req_sec_data,
447  [this, callback = std::move(callback)](const Bytes& resp_data) {
448  Bytes resp_sec_data;
449 
450  if VUNLIKELY (!this->impl_->security || !this->impl_->security->decrypt(resp_data, resp_sec_data)) {
451  VLOG_T("Client decrypt failed, url: ", this->impl_->url, ".");
452  return;
453  }
454 
455  this->invoke_callback(callback, resp_sec_data);
456  },
457  timeout);
458  } else {
459  this->impl_->try_record(ActionType::kClientRequest, req_data);
460 
461  if VUNLIKELY (!callback) {
462  return this->impl_->call(req_data, nullptr, timeout);
463  }
464 
465  return this->impl_->call(
466  req_data,
467  [this, callback = std::move(callback)](const Bytes& resp_data) {
468  this->impl_->try_record(ActionType::kClientResponse, resp_data);
469 
470  this->invoke_callback(callback, resp_data);
471  },
472  timeout);
473  }
474 }
475 
476 template <typename ReqT, typename RespT>
477 template <typename SecurityConfigT>
479  const std::string& url_str, SecurityConfigT&& sec_cfg, InitType type) {
480  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
481  "SecurityConfigT must be Security::Config.");
482 
483  return std::make_unique<SecurityClient<ReqT, RespT>>(url_str, std::forward<SecurityConfigT>(sec_cfg), type);
484 }
485 
486 template <typename ReqT, typename RespT>
487 template <typename SecurityConfigT>
489  const std::string& url_str, SecurityConfigT&& sec_cfg, InitType type) {
490  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
491  "SecurityConfigT must be Security::Config.");
492 
493  return std::make_shared<SecurityClient<ReqT, RespT>>(url_str, std::forward<SecurityConfigT>(sec_cfg), type);
494 }
495 
496 template <typename ReqT, typename RespT>
497 template <typename ConfT, typename SecurityConfigT, typename>
498 inline SecurityClient<ReqT, RespT>::SecurityClient(const ConfT& conf, SecurityConfigT&& sec_cfg, InitType type)
499  : Client<ReqT, RespT, SecurityType::kWithSecurity>(conf, InitType::kWithoutInit) {
500  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
501  "SecurityConfigT must be Security::Config.");
502 
503  this->enable_security(std::forward<SecurityConfigT>(sec_cfg));
504 
505  if VLIKELY (type == InitType::kWithInit) {
506  this->init(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
507  }
508 }
509 
510 template <typename ReqT, typename RespT>
511 template <typename SecurityConfigT>
512 inline SecurityClient<ReqT, RespT>::SecurityClient(const std::string& url_str, SecurityConfigT&& sec_cfg, InitType type)
513  : Client<ReqT, RespT, SecurityType::kWithSecurity>(url_str, InitType::kWithoutInit) {
514  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
515  "SecurityConfigT must be Security::Config.");
516 
517  this->enable_security(std::forward<SecurityConfigT>(sec_cfg));
518 
519  if VLIKELY (type == InitType::kWithInit) {
520  this->init(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
521  }
522 }
523 
524 } // namespace vlink
#define VLOG_F(...)
Definition: logger.h:791
#define VLOG_W(...)
Definition: logger.h:787
#define VLOG_T(...)
Definition: logger.h:781
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284