1use std::env;
16use std::sync::atomic::AtomicBool;
17use std::sync::atomic::Ordering;
18use std::sync::Arc;
19use std::time::Duration;
20
21use cheetah_string::CheetahString;
22use rocketmq_common::common::message::message_queue::MessageQueue;
23use rocketmq_common::common::tls_config::TlsConfig;
24use rocketmq_common::utils::name_server_address_utils::NameServerAddressUtils;
25use rocketmq_common::utils::network_util::NetworkUtil;
26use rocketmq_common::TimeUtils::current_nano;
27use rocketmq_remoting::protocol::namespace_util::NamespaceUtil;
28use rocketmq_remoting::protocol::request_type::RequestType;
29use rocketmq_remoting::protocol::LanguageCode;
30
31use crate::base::access_channel::AccessChannel;
32
33#[derive(Clone)]
34pub struct ClientConfig {
35 pub namesrv_addr: Option<CheetahString>,
36 pub client_ip: Option<CheetahString>,
37 pub instance_name: CheetahString,
38 pub client_callback_executor_threads: usize,
39 pub namespace: Option<CheetahString>,
40 pub namespace_initialized: Arc<AtomicBool>,
41 pub namespace_v2: Option<CheetahString>,
42 pub access_channel: AccessChannel,
43 pub poll_name_server_interval: u32,
44 pub heartbeat_broker_interval: u32,
45 pub persist_consumer_offset_interval: u32,
46 pub pull_time_delay_millis_when_exception: u32,
47 pub unit_mode: bool,
48 pub unit_name: Option<CheetahString>,
49 pub decode_read_body: bool,
50 pub decode_decompress_body: bool,
51 pub vip_channel_enabled: bool,
52 pub use_heartbeat_v2: bool,
53 pub enable_concurrent_heartbeat: bool,
54 pub use_tls: bool,
55 pub tls_config: TlsConfig,
56 pub socks_proxy_config: CheetahString,
57 pub mq_client_api_timeout: u64,
58 pub detect_timeout: u32,
59 pub detect_interval: u32,
60 pub language: LanguageCode,
61 pub enable_stream_request_type: bool,
62 pub send_latency_enable: bool,
63 pub start_detector_enable: bool,
64 pub enable_heartbeat_channel_event_listener: bool,
65 pub enable_trace: bool,
66 pub trace_topic: Option<CheetahString>,
67 pub trace_msg_batch_num: usize,
68 pub max_page_size_in_get_metadata: usize,
69 pub concurrent_heartbeat_thread_pool_size: usize,
74}
75
76impl Default for ClientConfig {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl ClientConfig {
83 pub const SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY: &'static str = "com.rocketmq.sendMessageWithVIPChannel";
84 pub const SOCKS_PROXY_CONFIG: &'static str = "com.rocketmq.socks.proxy.config";
85 pub const DECODE_READ_BODY: &'static str = "com.rocketmq.read.body";
86 pub const DECODE_DECOMPRESS_BODY: &'static str = "com.rocketmq.decompress.body";
87 pub const SEND_LATENCY_ENABLE: &'static str = "com.rocketmq.sendLatencyEnable";
88 pub const START_DETECTOR_ENABLE: &'static str = "com.rocketmq.startDetectorEnable";
89 pub const HEART_BEAT_V2: &'static str = "com.rocketmq.heartbeat.v2";
90 pub const ENABLE_CONCURRENT_HEARTBEAT: &'static str = "com.rocketmq.enableConcurrentHeartbeat";
91
92 pub fn new() -> Self {
93 ClientConfig {
94 namesrv_addr: NameServerAddressUtils::get_name_server_addresses().map(|addr| addr.into()),
95 client_ip: NetworkUtil::get_local_address().map(|addr| addr.into()),
96 instance_name: env::var("rocketmq.client.name")
97 .unwrap_or_else(|_| "DEFAULT".to_string())
98 .into(),
99 client_callback_executor_threads: num_cpus::get(),
100 namespace: None,
101 namespace_initialized: Arc::new(AtomicBool::new(false)),
102 namespace_v2: None,
103 access_channel: AccessChannel::Local,
104 poll_name_server_interval: Duration::from_secs(30).as_millis() as u32,
105 heartbeat_broker_interval: Duration::from_secs(30).as_millis() as u32,
106 persist_consumer_offset_interval: Duration::from_secs(5).as_millis() as u32,
107 pull_time_delay_millis_when_exception: 1000,
108 unit_mode: false,
109 unit_name: None,
110 decode_read_body: env::var(Self::DECODE_READ_BODY)
111 .unwrap_or_else(|_| "true".to_string())
112 .parse::<bool>()
113 .unwrap_or(true),
114 decode_decompress_body: env::var(Self::DECODE_DECOMPRESS_BODY)
115 .unwrap_or_else(|_| "true".to_string())
116 .parse::<bool>()
117 .unwrap_or(true),
118 vip_channel_enabled: env::var(Self::SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY)
119 .unwrap_or_else(|_| "false".to_string())
120 .parse::<bool>()
121 .unwrap_or(false),
122 use_heartbeat_v2: env::var(Self::HEART_BEAT_V2)
123 .unwrap_or_else(|_| "false".to_string())
124 .parse::<bool>()
125 .unwrap_or(false),
126 enable_concurrent_heartbeat: env::var(Self::ENABLE_CONCURRENT_HEARTBEAT)
127 .unwrap_or_else(|_| "false".to_string())
128 .parse::<bool>()
129 .unwrap_or(false),
130 use_tls: false,
131 tls_config: TlsConfig::default(),
132 socks_proxy_config: env::var(Self::SOCKS_PROXY_CONFIG)
133 .unwrap_or_else(|_| "{}".to_string())
134 .into(),
135 mq_client_api_timeout: Duration::from_secs(3).as_millis() as u64,
136 detect_timeout: 200,
137 detect_interval: Duration::from_secs(2).as_millis() as u32,
138 language: LanguageCode::RUST,
139 enable_stream_request_type: false,
140 send_latency_enable: env::var(Self::SEND_LATENCY_ENABLE)
141 .unwrap_or_else(|_| "false".to_string())
142 .parse::<bool>()
143 .unwrap_or(false),
144 start_detector_enable: env::var(Self::START_DETECTOR_ENABLE)
145 .unwrap_or_else(|_| "false".to_string())
146 .parse::<bool>()
147 .unwrap_or(false),
148 enable_heartbeat_channel_event_listener: true,
149 enable_trace: false,
150 trace_topic: None,
151 trace_msg_batch_num: 10,
152 max_page_size_in_get_metadata: 2000,
153 concurrent_heartbeat_thread_pool_size: num_cpus::get(),
154 }
155 }
156}
157
158impl ClientConfig {
159 #[inline]
160 pub fn with_namespace(&mut self, resource: impl Into<CheetahString>) -> CheetahString {
161 let resource = resource.into();
162 let namespace = self.get_namespace().unwrap_or_default();
163
164 if namespace.is_empty() {
166 return resource;
167 }
168
169 if NamespaceUtil::is_already_with_namespace(resource.as_str(), namespace.as_str()) {
171 return resource;
172 }
173
174 NamespaceUtil::wrap_namespace(namespace, resource)
175 }
176
177 #[inline]
178 pub fn queue_with_namespace(&mut self, mut queue: MessageQueue) -> MessageQueue {
179 if let Some(namespace) = self.get_namespace() {
180 if !namespace.is_empty() {
181 let topic = NamespaceUtil::wrap_namespace(namespace.as_str(), queue.topic_str());
182 queue.set_topic(topic);
183 return queue;
184 }
185 }
186 queue
187 }
188
189 #[inline]
190 pub fn queues_with_namespace<I>(&mut self, queues: I) -> Vec<MessageQueue>
191 where
192 I: IntoIterator<Item = MessageQueue>,
193 {
194 queues
195 .into_iter()
196 .map(|queue| self.queue_with_namespace(queue))
197 .collect()
198 }
199
200 #[inline]
201 pub fn get_namespace(&mut self) -> Option<CheetahString> {
202 let namespace_initialized = self.namespace_initialized.load(Ordering::Acquire);
203 if namespace_initialized {
204 return self.namespace.clone();
205 }
206
207 if let Some(ref namespace) = self.namespace {
208 return Some(namespace.clone());
209 }
210
211 if let Some(ref namesrv_addr) = self.namesrv_addr {
212 if NameServerAddressUtils::validate_instance_endpoint(namesrv_addr.as_ref()) {
213 self.namespace =
214 NameServerAddressUtils::parse_instance_id_from_endpoint(namesrv_addr.as_ref()).map(|id| id.into());
215 }
216 }
217 self.namespace_initialized.store(true, Ordering::Release);
218 self.namespace.clone()
219 }
220
221 #[inline]
222 pub fn change_instance_name_to_pid(&mut self) {
223 if self.instance_name == "DEFAULT" {
224 self.instance_name = format!("{}#{}", std::process::id(), current_nano()).into();
225 }
226 }
227 #[inline]
228 pub fn set_instance_name(&mut self, instance_name: CheetahString) {
229 self.instance_name = instance_name;
230 }
231 #[inline]
232 pub fn set_namesrv_addr(&mut self, namesrv_addr: CheetahString) {
233 self.namesrv_addr = Some(namesrv_addr);
234 self.namespace_initialized.store(false, Ordering::Release);
235 }
236
237 #[inline]
238 pub fn build_mq_client_id(&self) -> String {
239 let estimated_capacity = self.client_ip.as_ref().map(|ip| ip.len()).unwrap_or(15)
241 + self.instance_name.len()
242 + self.unit_name.as_ref().map(|un| un.len() + 1).unwrap_or(0)
243 + if self.enable_stream_request_type { 8 } else { 0 }
244 + 3; let mut sb = String::with_capacity(estimated_capacity);
247 if let Some(ref client_ip) = self.client_ip {
248 sb.push_str(client_ip.as_str());
249 }
250
251 sb.push('@');
252 sb.push_str(self.instance_name.as_str());
253 if let Some(ref unit_name) = self.unit_name {
254 if !unit_name.is_empty() {
255 sb.push('@');
256 sb.push_str(unit_name.as_str());
257 }
258 }
259
260 if self.enable_stream_request_type {
261 sb.push('@');
262 sb.push_str(RequestType::Stream.to_string().as_str());
263 }
264
265 sb
266 }
267
268 #[inline]
269 pub fn get_namesrv_addr(&self) -> Option<CheetahString> {
270 if let Some(namesrv_addr) = self
271 .namesrv_addr
272 .as_ref()
273 .filter(|addr| !addr.is_empty() && NameServerAddressUtils::is_name_srv_endpoint(addr.as_str()))
274 {
275 NameServerAddressUtils::get_name_srv_addr_from_namesrv_endpoint(namesrv_addr.as_str())
276 .map(|addr| addr.into())
277 } else {
278 self.namesrv_addr.clone()
279 }
280 }
281
282 #[inline]
285 pub fn get_client_ip(&self) -> Option<&CheetahString> {
286 self.client_ip.as_ref()
287 }
288
289 #[inline]
290 pub fn set_client_ip(&mut self, client_ip: CheetahString) {
291 self.client_ip = Some(client_ip);
292 }
293
294 #[inline]
295 pub fn get_instance_name(&self) -> &CheetahString {
296 &self.instance_name
297 }
298
299 #[inline]
300 pub fn get_client_callback_executor_threads(&self) -> usize {
301 self.client_callback_executor_threads
302 }
303
304 #[inline]
305 pub fn set_client_callback_executor_threads(&mut self, threads: usize) {
306 self.client_callback_executor_threads = threads;
307 }
308
309 #[inline]
310 pub fn get_namespace_v2(&self) -> Option<&CheetahString> {
311 self.namespace_v2.as_ref()
312 }
313
314 #[inline]
315 pub fn set_namespace_v2(&mut self, namespace_v2: CheetahString) {
316 self.namespace_v2 = Some(namespace_v2);
317 }
318
319 #[inline]
320 pub fn set_namespace(&mut self, namespace: CheetahString) {
321 self.namespace = Some(namespace);
322 self.namespace_initialized.store(true, Ordering::Release);
323 }
324
325 #[inline]
326 pub fn get_access_channel(&self) -> AccessChannel {
327 self.access_channel
328 }
329
330 #[inline]
331 pub fn set_access_channel(&mut self, access_channel: AccessChannel) {
332 self.access_channel = access_channel;
333 }
334
335 #[inline]
336 pub fn get_poll_name_server_interval(&self) -> u32 {
337 self.poll_name_server_interval
338 }
339
340 #[inline]
341 pub fn set_poll_name_server_interval(&mut self, interval: u32) {
342 self.poll_name_server_interval = interval;
343 }
344
345 #[inline]
346 pub fn get_heartbeat_broker_interval(&self) -> u32 {
347 self.heartbeat_broker_interval
348 }
349
350 #[inline]
351 pub fn set_heartbeat_broker_interval(&mut self, interval: u32) {
352 self.heartbeat_broker_interval = interval;
353 }
354
355 #[inline]
356 pub fn get_persist_consumer_offset_interval(&self) -> u32 {
357 self.persist_consumer_offset_interval
358 }
359
360 #[inline]
361 pub fn set_persist_consumer_offset_interval(&mut self, interval: u32) {
362 self.persist_consumer_offset_interval = interval;
363 }
364
365 #[inline]
366 pub fn get_pull_time_delay_millis_when_exception(&self) -> u32 {
367 self.pull_time_delay_millis_when_exception
368 }
369
370 #[inline]
371 pub fn get_pull_time_delay_mills_when_exception(&self) -> u32 {
372 self.get_pull_time_delay_millis_when_exception()
373 }
374
375 #[inline]
376 pub fn set_pull_time_delay_millis_when_exception(&mut self, delay: u32) {
377 self.pull_time_delay_millis_when_exception = delay;
378 }
379
380 #[inline]
381 pub fn set_pull_time_delay_mills_when_exception(&mut self, delay: u32) {
382 self.set_pull_time_delay_millis_when_exception(delay);
383 }
384
385 #[inline]
386 pub fn get_unit_name(&self) -> Option<&CheetahString> {
387 self.unit_name.as_ref()
388 }
389
390 #[inline]
391 pub fn set_unit_name(&mut self, unit_name: CheetahString) {
392 self.unit_name = Some(unit_name);
393 }
394
395 #[inline]
396 pub fn is_unit_mode(&self) -> bool {
397 self.unit_mode
398 }
399
400 #[inline]
401 pub fn set_unit_mode(&mut self, unit_mode: bool) {
402 self.unit_mode = unit_mode;
403 }
404
405 #[inline]
406 pub fn is_decode_read_body(&self) -> bool {
407 self.decode_read_body
408 }
409
410 #[inline]
411 pub fn set_decode_read_body(&mut self, decode_read_body: bool) {
412 self.decode_read_body = decode_read_body;
413 }
414
415 #[inline]
416 pub fn is_decode_decompress_body(&self) -> bool {
417 self.decode_decompress_body
418 }
419
420 #[inline]
421 pub fn set_decode_decompress_body(&mut self, decode_decompress_body: bool) {
422 self.decode_decompress_body = decode_decompress_body;
423 }
424
425 #[inline]
426 pub fn is_vip_channel_enabled(&self) -> bool {
427 self.vip_channel_enabled
428 }
429
430 #[inline]
431 pub fn set_vip_channel_enabled(&mut self, enabled: bool) {
432 self.vip_channel_enabled = enabled;
433 }
434
435 #[inline]
436 pub fn is_use_heartbeat_v2(&self) -> bool {
437 self.use_heartbeat_v2
438 }
439
440 #[inline]
441 pub fn set_use_heartbeat_v2(&mut self, use_heartbeat_v2: bool) {
442 self.use_heartbeat_v2 = use_heartbeat_v2;
443 }
444
445 #[inline]
446 pub fn is_use_tls(&self) -> bool {
447 self.use_tls
448 }
449
450 #[inline]
451 pub fn set_use_tls(&mut self, use_tls: bool) {
452 self.use_tls = use_tls;
453 self.tls_config.enable = use_tls;
454 }
455
456 #[inline]
457 pub fn tls_config(&self) -> &TlsConfig {
458 &self.tls_config
459 }
460
461 #[inline]
462 pub fn set_tls_config(&mut self, mut tls_config: TlsConfig) {
463 tls_config.enable = self.use_tls;
464 self.tls_config = tls_config;
465 }
466
467 #[inline]
468 pub fn set_tls_test_mode_enable(&mut self, enabled: bool) {
469 self.tls_config.test_mode_enable = enabled;
470 }
471
472 #[inline]
473 pub fn set_tls_client_auth_server(&mut self, enabled: bool) {
474 self.tls_config.client.auth_server = enabled;
475 }
476
477 #[inline]
478 pub fn set_tls_client_trust_cert_path(&mut self, path: impl Into<String>) {
479 self.tls_config.client.trust_cert_path = Some(path.into());
480 }
481
482 #[inline]
483 pub fn set_tls_client_cert_path(&mut self, path: impl Into<String>) {
484 self.tls_config.client.cert_path = Some(path.into());
485 }
486
487 #[inline]
488 pub fn set_tls_client_key_path(&mut self, path: impl Into<String>) {
489 self.tls_config.client.key_path = Some(path.into());
490 }
491
492 #[inline]
493 pub fn get_socks_proxy_config(&self) -> &CheetahString {
494 &self.socks_proxy_config
495 }
496
497 #[inline]
498 pub fn set_socks_proxy_config(&mut self, config: CheetahString) {
499 self.socks_proxy_config = config;
500 }
501
502 #[inline]
503 pub fn get_language(&self) -> LanguageCode {
504 self.language
505 }
506
507 #[inline]
508 pub fn set_language(&mut self, language: LanguageCode) {
509 self.language = language;
510 }
511
512 #[inline]
513 pub fn get_mq_client_api_timeout(&self) -> u64 {
514 self.mq_client_api_timeout
515 }
516
517 #[inline]
518 pub fn set_mq_client_api_timeout(&mut self, timeout: u64) {
519 self.mq_client_api_timeout = timeout;
520 }
521
522 #[inline]
523 pub fn get_detect_timeout(&self) -> u32 {
524 self.detect_timeout
525 }
526
527 #[inline]
528 pub fn set_detect_timeout(&mut self, timeout: u32) {
529 self.detect_timeout = timeout;
530 }
531
532 #[inline]
533 pub fn get_detect_interval(&self) -> u32 {
534 self.detect_interval
535 }
536
537 #[inline]
538 pub fn set_detect_interval(&mut self, interval: u32) {
539 self.detect_interval = interval;
540 }
541
542 #[inline]
543 pub fn is_enable_stream_request_type(&self) -> bool {
544 self.enable_stream_request_type
545 }
546
547 #[inline]
548 pub fn set_enable_stream_request_type(&mut self, enabled: bool) {
549 self.enable_stream_request_type = enabled;
550 }
551
552 #[inline]
553 pub fn is_send_latency_enable(&self) -> bool {
554 self.send_latency_enable
555 }
556
557 #[inline]
558 pub fn set_send_latency_enable(&mut self, enabled: bool) {
559 self.send_latency_enable = enabled;
560 }
561
562 #[inline]
563 pub fn is_start_detector_enable(&self) -> bool {
564 self.start_detector_enable
565 }
566
567 #[inline]
568 pub fn set_start_detector_enable(&mut self, enabled: bool) {
569 self.start_detector_enable = enabled;
570 }
571
572 #[inline]
573 pub fn is_enable_heartbeat_channel_event_listener(&self) -> bool {
574 self.enable_heartbeat_channel_event_listener
575 }
576
577 #[inline]
578 pub fn set_enable_heartbeat_channel_event_listener(&mut self, enabled: bool) {
579 self.enable_heartbeat_channel_event_listener = enabled;
580 }
581
582 #[inline]
583 pub fn is_enable_trace(&self) -> bool {
584 self.enable_trace
585 }
586
587 #[inline]
588 pub fn set_enable_trace(&mut self, enabled: bool) {
589 self.enable_trace = enabled;
590 }
591
592 #[inline]
593 pub fn get_trace_topic(&self) -> Option<&CheetahString> {
594 self.trace_topic.as_ref()
595 }
596
597 #[inline]
598 pub fn set_trace_topic(&mut self, topic: CheetahString) {
599 self.trace_topic = Some(topic);
600 }
601
602 #[inline]
603 pub fn get_trace_msg_batch_num(&self) -> usize {
604 self.trace_msg_batch_num
605 }
606
607 #[inline]
608 pub fn set_trace_msg_batch_num(&mut self, num: usize) {
609 self.trace_msg_batch_num = num;
610 }
611
612 #[inline]
613 pub fn get_max_page_size_in_get_metadata(&self) -> usize {
614 self.max_page_size_in_get_metadata
615 }
616
617 #[inline]
618 pub fn set_max_page_size_in_get_metadata(&mut self, size: usize) {
619 self.max_page_size_in_get_metadata = size;
620 }
621
622 #[inline]
623 pub fn get_concurrent_heartbeat_thread_pool_size(&self) -> usize {
624 self.concurrent_heartbeat_thread_pool_size
625 }
626
627 #[inline]
628 pub fn is_enable_concurrent_heartbeat(&self) -> bool {
629 self.enable_concurrent_heartbeat
630 }
631
632 #[inline]
633 pub fn set_enable_concurrent_heartbeat(&mut self, enabled: bool) {
634 self.enable_concurrent_heartbeat = enabled;
635 }
636
637 #[inline]
638 pub fn set_concurrent_heartbeat_thread_pool_size(&mut self, size: usize) {
639 self.concurrent_heartbeat_thread_pool_size = size;
640 }
641
642 #[inline]
646 pub fn clone_client_config(&self) -> Self {
647 self.clone()
648 }
649
650 pub fn reset_client_config(&mut self, other: &ClientConfig) {
652 self.namesrv_addr = other.namesrv_addr.clone();
653 self.client_ip = other.client_ip.clone();
654 self.instance_name = other.instance_name.clone();
655 self.client_callback_executor_threads = other.client_callback_executor_threads;
656 self.namespace = other.namespace.clone();
657 self.namespace_v2 = other.namespace_v2.clone();
658 self.access_channel = other.access_channel;
659 self.poll_name_server_interval = other.poll_name_server_interval;
660 self.heartbeat_broker_interval = other.heartbeat_broker_interval;
661 self.persist_consumer_offset_interval = other.persist_consumer_offset_interval;
662 self.pull_time_delay_millis_when_exception = other.pull_time_delay_millis_when_exception;
663 self.unit_mode = other.unit_mode;
664 self.unit_name = other.unit_name.clone();
665 self.decode_read_body = other.decode_read_body;
666 self.decode_decompress_body = other.decode_decompress_body;
667 self.vip_channel_enabled = other.vip_channel_enabled;
668 self.use_heartbeat_v2 = other.use_heartbeat_v2;
669 self.use_tls = other.use_tls;
670 self.tls_config = other.tls_config.clone();
671 self.socks_proxy_config = other.socks_proxy_config.clone();
672 self.language = other.language;
673 self.mq_client_api_timeout = other.mq_client_api_timeout;
674 self.detect_timeout = other.detect_timeout;
675 self.detect_interval = other.detect_interval;
676 self.enable_stream_request_type = other.enable_stream_request_type;
677 self.send_latency_enable = other.send_latency_enable;
678 self.start_detector_enable = other.start_detector_enable;
679 self.enable_heartbeat_channel_event_listener = other.enable_heartbeat_channel_event_listener;
680 self.enable_trace = other.enable_trace;
681 self.trace_topic = other.trace_topic.clone();
682 self.trace_msg_batch_num = other.trace_msg_batch_num;
683 self.max_page_size_in_get_metadata = other.max_page_size_in_get_metadata;
684 self.enable_concurrent_heartbeat = other.enable_concurrent_heartbeat;
685 self.concurrent_heartbeat_thread_pool_size = other.concurrent_heartbeat_thread_pool_size;
686 }
687
688 #[inline]
690 #[deprecated(note = "Use with_namespace for namespace wrapping")]
691 pub fn without_namespace(&mut self, resource: &str) -> CheetahString {
692 if let Some(namespace) = self.get_namespace().as_deref() {
693 NamespaceUtil::without_namespace_with_namespace(resource, namespace).into()
694 } else {
695 NamespaceUtil::without_namespace(resource).into()
696 }
697 }
698
699 #[inline]
714 pub fn builder() -> crate::base::client_config_builder::ClientConfigBuilder {
715 crate::base::client_config_builder::ClientConfigBuilder::new()
716 }
717}
718
719impl std::fmt::Display for ClientConfig {
720 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
721 write!(
722 f,
723 "ClientConfig {{ namesrv_addr: {:?}, client_ip: {:?}, instance_name: {}, \
724 client_callback_executor_threads: {}, namespace: {:?}, namespace_v2: {:?}, access_channel: {}, \
725 poll_name_server_interval: {}, heartbeat_broker_interval: {}, persist_consumer_offset_interval: {}, \
726 pull_time_delay_millis_when_exception: {}, unit_mode: {}, unit_name: {:?}, decode_read_body: {}, \
727 decode_decompress_body: {}, vip_channel_enabled: {}, use_heartbeat_v2: {}, use_tls: {}, \
728 socks_proxy_config: {}, mq_client_api_timeout: {}, detect_timeout: {}, detect_interval: {}, language: \
729 {:?}, enable_stream_request_type: {}, send_latency_enable: {}, start_detector_enable: {}, \
730 enable_heartbeat_channel_event_listener: {}, enable_trace: {}, trace_topic: {:?}, trace_msg_batch_num: \
731 {}, max_page_size_in_get_metadata: {}, enable_concurrent_heartbeat: {}, \
732 concurrent_heartbeat_thread_pool_size: {} }}",
733 self.namesrv_addr,
734 self.client_ip,
735 self.instance_name,
736 self.client_callback_executor_threads,
737 self.namespace,
738 self.namespace_v2,
739 self.access_channel,
740 self.poll_name_server_interval,
741 self.heartbeat_broker_interval,
742 self.persist_consumer_offset_interval,
743 self.pull_time_delay_millis_when_exception,
744 self.unit_mode,
745 self.unit_name,
746 self.decode_read_body,
747 self.decode_decompress_body,
748 self.vip_channel_enabled,
749 self.use_heartbeat_v2,
750 self.use_tls,
751 self.socks_proxy_config,
752 self.mq_client_api_timeout,
753 self.detect_timeout,
754 self.detect_interval,
755 self.language,
756 self.enable_stream_request_type,
757 self.send_latency_enable,
758 self.start_detector_enable,
759 self.enable_heartbeat_channel_event_listener,
760 self.enable_trace,
761 self.trace_topic,
762 self.trace_msg_batch_num,
763 self.max_page_size_in_get_metadata,
764 self.enable_concurrent_heartbeat,
765 self.concurrent_heartbeat_thread_pool_size
766 )
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 #[test]
775 fn java_compatible_aliases_delegate_to_client_config_fields() {
776 let mut config = ClientConfig::default();
777
778 config.set_pull_time_delay_mills_when_exception(2345);
779 assert_eq!(config.get_pull_time_delay_mills_when_exception(), 2345);
780 assert_eq!(config.get_pull_time_delay_millis_when_exception(), 2345);
781
782 config.set_enable_concurrent_heartbeat(false);
783 assert!(!config.is_enable_concurrent_heartbeat());
784 config.set_enable_concurrent_heartbeat(true);
785 assert!(config.is_enable_concurrent_heartbeat());
786 }
787
788 #[test]
789 fn reset_client_config_copies_modern_java_fields() {
790 let mut source = ClientConfig::default();
791 source.set_use_tls(true);
792 source.set_enable_trace(true);
793 source.set_trace_topic(CheetahString::from("TraceTopicA"));
794 source.set_trace_msg_batch_num(32);
795 source.set_max_page_size_in_get_metadata(4096);
796 source.set_enable_concurrent_heartbeat(true);
797 source.set_concurrent_heartbeat_thread_pool_size(8);
798
799 let mut target = ClientConfig::default();
800 target.set_use_tls(false);
801 target.set_enable_trace(false);
802 target.trace_topic = None;
803 target.set_trace_msg_batch_num(10);
804 target.set_max_page_size_in_get_metadata(2000);
805 target.set_enable_concurrent_heartbeat(false);
806 target.set_concurrent_heartbeat_thread_pool_size(1);
807
808 target.reset_client_config(&source);
809
810 assert!(target.is_use_tls());
811 assert!(target.tls_config().enable);
812 assert!(target.is_enable_trace());
813 assert_eq!(
814 target.get_trace_topic().map(|topic| topic.as_str()),
815 Some("TraceTopicA")
816 );
817 assert_eq!(target.get_trace_msg_batch_num(), 32);
818 assert_eq!(target.get_max_page_size_in_get_metadata(), 4096);
819 assert!(target.is_enable_concurrent_heartbeat());
820 assert_eq!(target.get_concurrent_heartbeat_thread_pool_size(), 8);
821 }
822
823 #[test]
824 fn display_uses_java_access_channel_name() {
825 let mut config = ClientConfig::default();
826 config.set_access_channel(AccessChannel::Cloud);
827
828 let rendered = config.to_string();
829
830 assert!(rendered.contains("access_channel: CLOUD"));
831 assert!(!rendered.contains("access_channel: Cloud"));
832 }
833
834 #[test]
835 fn queues_with_namespace_wraps_each_queue_like_java_collection_helper() {
836 let mut config = ClientConfig::default();
837 let queues = vec![
838 MessageQueue::from_parts("topic_a", "broker-a", 0),
839 MessageQueue::from_parts("ns%topic_b", "broker-a", 1),
840 ];
841
842 let unchanged = config.queues_with_namespace(queues.clone());
843 assert_eq!(unchanged[0].topic_str(), "topic_a");
844 assert_eq!(unchanged[1].topic_str(), "ns%topic_b");
845
846 config.namespace = Some(CheetahString::from("ns"));
847 config.namespace_initialized.store(false, Ordering::Release);
848
849 let wrapped = config.queues_with_namespace(queues);
850 assert_eq!(wrapped[0].topic_str(), "ns%topic_a");
851 assert_eq!(wrapped[1].topic_str(), "ns%topic_b");
852 }
853}