VLink  2.0.0
A high-performance communication middleware
subscriber-inl.h
Go to the documentation of this file.
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 "../impl/url.h"
33 #include "../serializer.h"
34 #include "../subscriber.h"
35 
36 namespace vlink {
37 
38 template <typename MsgT, SecurityType SecT>
40  InitType type) {
41  return std::make_unique<Subscriber<MsgT, SecT>>(url_str, type);
42 }
43 
44 template <typename MsgT, SecurityType SecT>
46  InitType type) {
47  return std::make_shared<Subscriber<MsgT, SecT>>(url_str, type);
48 }
49 
50 template <typename MsgT, SecurityType SecT>
51 template <typename ConfT, typename>
52 inline Subscriber<MsgT, SecT>::Subscriber(const ConfT& conf, InitType type) {
53  static_assert(ConfT::get_allow_impl_type() & kImplType, "Conf does not support subscriber mode.");
54 
55  if VUNLIKELY (!conf.parse(kImplType) || !conf.is_valid()) {
56  VLOG_F(conf, " subscriber configuration is invalid or could not be parsed.");
57  return;
58  }
59 
60  this->impl_ = conf.create_subscriber();
61 
62  if VUNLIKELY (!this->impl_) {
63  VLOG_F(conf, " subscriber implementation not available for this transport.");
64  return;
65  }
66 
67  this->impl_->transport_type = conf.get_transport_type();
68  this->impl_->ser_type = Serializer::get_serialized_type<kMsgType, MsgT>();
69  this->impl_->schema_type = Serializer::get_schema_type<kMsgType, MsgT>();
70  this->impl_->is_cdr_type = Serializer::is_cdr_type<MsgT>();
71 
72  if constexpr (std::is_same_v<ConfT, Url>) {
73  this->impl_->url = conf.get_str();
74  }
75 
76  if constexpr (SecT == SecurityType::kWithSecurity) {
77  this->impl_->is_security_type = true;
78  }
79 
80  if VLIKELY (type == InitType::kWithInit) {
81  this->init(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
82  }
83 }
84 
85 template <typename MsgT, SecurityType SecT>
86 inline Subscriber<MsgT, SecT>::Subscriber(const std::string& url_str, InitType type)
87  : Subscriber<MsgT, SecT>(Url(url_str), type) {}
88 
89 template <typename MsgT, SecurityType SecT>
91  if constexpr (Traits::IsSharedPtr<MsgT>()) {
92  if constexpr (std::is_base_of_v<IntraDataType, typename MsgT::element_type>) {
93  static_assert(SecT != SecurityType::kWithSecurity, "IntraData must without security.");
94 
95  if (this->impl_->transport_type == TransportType::kIntra) {
96  return listen_intra(std::move(callback));
97  }
98  }
99  }
100 
101  return listen_bytes([this, callback = std::move(callback)](const Bytes& data) {
102 #ifndef VLINK_DISABLE_PROFILER
103  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
104 #endif
105 
106  if constexpr (std::is_same_v<MsgT, Bytes>) {
107  (void)this;
108 
109  callback(data);
110  } else {
111  thread_local auto msg = this->template get_default_value<MsgT>();
112 
113  if VUNLIKELY (!Serializer::deserialize<kMsgType>(data, msg, this->impl_->transport_type)) {
114  VLOG_T("Subscriber deserialize failed, url: ", this->impl_->url, ".");
115  return;
116  }
117 
118  callback(msg);
119  }
120  });
121 }
122 
123 template <typename MsgT, SecurityType SecT>
124 inline void Subscriber<MsgT, SecT>::set_manual_unloan(bool manual_unloan) {
125  this->impl_->set_manual_unloan(manual_unloan);
126  this->is_manual_unloan_ = manual_unloan;
127 }
128 
129 template <typename MsgT, SecurityType SecT>
131  this->impl_->set_latency_and_lost_enabled(enable);
132 }
133 
134 template <typename MsgT, SecurityType SecT>
136  return this->impl_->is_latency_and_lost_enabled();
137 }
138 
139 template <typename MsgT, SecurityType SecT>
140 inline int64_t Subscriber<MsgT, SecT>::get_latency() const {
141  return this->impl_->get_latency();
142 }
143 
144 template <typename MsgT, SecurityType SecT>
146  return this->impl_->get_lost();
147 }
148 
149 template <typename MsgT, SecurityType SecT>
151  if VUNLIKELY (this->has_inited_.load(std::memory_order_acquire)) {
152  this->impl_->deinit_ext();
153  this->impl_->impl_type = kGetter;
154  this->impl_->init_ext();
155  } else {
156  this->impl_->impl_type = kGetter;
157  }
158 }
159 
160 template <typename MsgT, SecurityType SecT>
162  if VUNLIKELY (!this->has_inited_.load(std::memory_order_acquire)) {
163  VLOG_F("Subscriber::listen_bytes() called before init().");
164  return false;
165  }
166 
167  if VUNLIKELY (this->impl_->is_listened) {
168  VLOG_F("Subscriber has already been listened, url: ", this->impl_->url, ".");
169  return false;
170  }
171 
172  bool ret = this->impl_->listen([this, callback = std::move(callback)](const Bytes& data) {
173  if constexpr (SecT == SecurityType::kWithSecurity) {
174  Bytes sec_data;
175 
176  if VUNLIKELY (!this->impl_->security || !this->impl_->security->decrypt(data, sec_data)) {
177  VLOG_T("Subscriber decrypt failed, url: ", this->impl_->url, ".");
178  return;
179  }
180 
181  this->invoke_callback(callback, sec_data);
182  } else {
183  this->impl_->try_record(ActionType::kSubscribe, data);
184 
185  this->invoke_callback(callback, data);
186  }
187  });
188 
189  this->impl_->is_listened = ret;
190 
191  return ret;
192 }
193 
194 template <typename MsgT, SecurityType SecT>
195 inline bool Subscriber<MsgT, SecT>::listen_intra(MsgCallback&& callback) {
196  if VUNLIKELY (!this->has_inited_.load(std::memory_order_acquire)) {
197  VLOG_F("Subscriber::listen_intra() called before init().");
198  return false;
199  }
200 
201  if VUNLIKELY (this->impl_->is_listened) {
202  VLOG_F("Subscriber has already been listened, url: ", this->impl_->url, ".");
203  return false;
204  }
205 
206  bool ret = this->impl_->listen([this, callback = std::move(callback)](const IntraData& intra_data) {
207 #ifndef VLINK_DISABLE_PROFILER
208  CpuProfilerGuard profiler_guard(this->impl_->profiler.get());
209 #endif
210 
211  if constexpr (Traits::IsSharedPtr<MsgT>()) {
212 #if defined(NDEBUG) || defined(__ANDROID__)
213  auto intra_msg = std::static_pointer_cast<typename MsgT::element_type>(intra_data);
214 #else
215  auto intra_msg = std::dynamic_pointer_cast<typename MsgT::element_type>(intra_data);
216 #endif
217 
218  if VLIKELY (intra_msg) {
219  MsgT typed_msg(std::move(intra_msg));
220  this->invoke_callback(callback, typed_msg);
221  } else {
222  VLOG_T("Subscriber get intra data failed, url: ", this->impl_->url, ".");
223  }
224  }
225  });
226 
227  this->impl_->is_listened = ret;
228 
229  return ret;
230 }
231 
232 template <typename MsgT>
233 template <typename SecurityConfigT>
235  SecurityConfigT&& sec_cfg,
236  InitType type) {
237  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
238  "SecurityConfigT must be Security::Config.");
239 
240  return std::make_unique<SecuritySubscriber<MsgT>>(url_str, std::forward<SecurityConfigT>(sec_cfg), type);
241 }
242 
243 template <typename MsgT>
244 template <typename SecurityConfigT>
246  SecurityConfigT&& sec_cfg,
247  InitType type) {
248  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
249  "SecurityConfigT must be Security::Config.");
250 
251  return std::make_shared<SecuritySubscriber<MsgT>>(url_str, std::forward<SecurityConfigT>(sec_cfg), type);
252 }
253 
254 template <typename MsgT>
255 template <typename ConfT, typename SecurityConfigT, typename>
256 inline SecuritySubscriber<MsgT>::SecuritySubscriber(const ConfT& conf, SecurityConfigT&& sec_cfg, InitType type)
258  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
259  "SecurityConfigT must be Security::Config.");
260 
261  this->enable_security(std::forward<SecurityConfigT>(sec_cfg));
262 
263  if VLIKELY (type == InitType::kWithInit) {
264  this->init(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
265  }
266 }
267 
268 template <typename MsgT>
269 template <typename SecurityConfigT>
270 inline SecuritySubscriber<MsgT>::SecuritySubscriber(const std::string& url_str, SecurityConfigT&& sec_cfg,
271  InitType type)
273  static_assert(std::is_same_v<std::decay_t<SecurityConfigT>, Security::Config>,
274  "SecurityConfigT must be Security::Config.");
275 
276  this->enable_security(std::forward<SecurityConfigT>(sec_cfg));
277 
278  if VLIKELY (type == InitType::kWithInit) {
279  this->init(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
280  }
281 }
282 
283 } // namespace vlink
#define VLOG_F(...)
Definition: logger.h:791
#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