1use super::admin::parse_lite_order_count_info_like_java;
16use super::admin::pop_msg_queue_offset_for_index;
17use super::admin::split_lite_dispatch_value;
18use super::callback_executor::ClientCallbackExecutor;
19use super::request_builder::notification_request;
20use super::response_decoder::notify_result_from_response;
21use super::*;
22use rocketmq_error::fields;
23use rocketmq_error::Error;
24use rocketmq_error::ErrorContext;
25use rocketmq_transport::api::OutboundRequestContractReason;
26use rocketmq_transport::api::OutboundRequestRejectionReason;
27use rocketmq_transport::api::OutboundRequestStage;
28
29fn pop_background_task_cancelled(actual: impl Into<String>) -> ClientError {
30 ClientError::invalid_state("active client POP request", actual.into())
31}
32
33fn pop_background_task_spawn_failed(source: impl std::error::Error + Send + Sync + 'static) -> ClientError {
34 let context = ErrorContext::new()
35 .with_text(fields::EXPECTED_STATE, "active client POP request")
36 .with_text(fields::ACTUAL_STATE, "client POP task spawn failed");
37 ClientError::from_error(
38 Error::caused_by(&rocketmq_error::CLIENT_LIFECYCLE_INVALID_STATE, source).with_context(context),
39 )
40}
41
42fn consumer_request_error(operation: &'static str, input: RetryInput) -> ClientError {
43 match input {
44 RetryInput::Transport(error) => ClientError::from_shared(error.into_shared_error()),
45 RetryInput::Rejected(rejection) => match rejection.reason() {
46 OutboundRequestRejectionReason::DeadlineExpired => {
47 let mut context = ErrorContext::new().with_text(fields::OPERATION_DIAGNOSTIC, operation);
48 if let Some(timeout_millis) = rejection.timeout_millis() {
49 context = context.with_u64(fields::TIMEOUT_MS, timeout_millis);
50 }
51 ClientError::from_shared(Arc::new(
52 Error::new(&rocketmq_error::CORE_OPERATION_TIMED_OUT).with_context(context),
53 ))
54 }
55 OutboundRequestRejectionReason::ClientStopping => ClientError::not_started(),
56 OutboundRequestRejectionReason::QueueSaturated => ClientError::from_shared(Arc::new(Error::new(
57 &rocketmq_error::TRANSPORT_ADMISSION_QUEUE_SATURATED,
58 ))),
59 OutboundRequestRejectionReason::Cancelled
60 | OutboundRequestRejectionReason::SessionClosed
61 | OutboundRequestRejectionReason::EndpointUnavailable => {
62 let phase = match rejection.reason() {
63 OutboundRequestRejectionReason::SessionClosed => "closed",
64 _ => consumer_request_stage_phase(rejection.stage()),
65 };
66 let mut context = ErrorContext::new().with_text(fields::PHASE, phase);
67 if rejection.remote_addr_present() {
68 context = context.with_secret_presence(fields::REMOTE_ADDR_PRESENT);
69 }
70 ClientError::from_shared(Arc::new(
71 Error::new(&rocketmq_error::TRANSPORT_CONNECTION_FAILED).with_context(context),
72 ))
73 }
74 },
75 RetryInput::Contract(contract) => match contract.reason() {
76 OutboundRequestContractReason::NameServerEndpointMissing => {
77 ClientError::from_shared(Arc::new(Error::new(&rocketmq_error::TRANSPORT_CONNECTION_FAILED)))
78 }
79 },
80 RetryInput::Response { terminal_error, .. } | RetryInput::BusinessError(terminal_error) => terminal_error,
81 RetryInput::SendStatus(_) | RetryInput::RouteUnavailable => {
83 unreachable!("consumer request returned an unrelated retry input")
84 }
85 }
86}
87
88const fn consumer_request_stage_phase(stage: OutboundRequestStage) -> &'static str {
89 match stage {
90 OutboundRequestStage::BeforeWrite => "before_write",
91 OutboundRequestStage::Writing => "writing",
92 OutboundRequestStage::AwaitingResponse => "awaiting_response",
93 OutboundRequestStage::ResponseReceived => "response_received",
94 }
95}
96
97pub struct ConsumerClient<'a> {
98 api: &'a MQClientAPIImpl,
99}
100
101impl ConsumerClient<'_> {
102 pub async fn consumer_offset(
103 &self,
104 addr: &str,
105 request_header: QueryConsumerOffsetRequestHeader,
106 timeout_millis: u64,
107 ) -> crate::ClientResult<i64> {
108 self.api
109 .query_consumer_offset(addr, request_header, timeout_millis)
110 .await
111 }
112}
113
114impl MQClientAPIImpl {
115 pub(super) fn spawn_pop_callback_task<PC, F>(
116 service_context: &ChildServiceContext,
117 tracker: &TaskTracker,
118 shutdown_token: &CancellationToken,
119 callback_executor: ClientCallbackExecutor,
120 request: F,
121 pop_callback: PC,
122 ) where
123 PC: PopCallback + 'static,
124 F: Future<Output = crate::ClientResult<PopResult>> + Send + 'static,
125 {
126 let callback = Arc::new(std::sync::Mutex::new(Some(pop_callback)));
127 if shutdown_token.is_cancelled() {
128 if let Some(mut callback) = callback
129 .lock()
130 .unwrap_or_else(std::sync::PoisonError::into_inner)
131 .take()
132 {
133 callback.on_error(pop_background_task_cancelled("client API is shutting down"));
134 }
135 return;
136 }
137
138 let task_callback = callback.clone();
139 let shutdown_token = shutdown_token.clone();
140 let tracked_task = tracker.track_future(async move {
141 let outcome = tokio::select! {
142 biased;
143 _ = shutdown_token.cancelled() => {
144 Err(pop_background_task_cancelled("client API shutdown cancelled the POP request"))
145 }
146 outcome = request => outcome,
147 };
148 let callback = task_callback
149 .lock()
150 .unwrap_or_else(std::sync::PoisonError::into_inner)
151 .take();
152 let Some(mut callback) = callback else {
153 return;
154 };
155 let _ = callback_executor
156 .execute(async move {
157 match outcome {
158 Ok(pop_result) => callback.on_success(pop_result).await,
159 Err(error) => callback.on_error(error),
160 }
161 })
162 .await;
163 });
164
165 if let Err(error) = spawn_client_task_with_context(
166 service_context,
167 "rocketmq-client-pop-message-async",
168 Box::pin(tracked_task),
169 ) {
170 if let Some(mut callback) = callback
171 .lock()
172 .unwrap_or_else(std::sync::PoisonError::into_inner)
173 .take()
174 {
175 callback.on_error(pop_background_task_spawn_failed(error));
176 }
177 }
178 }
179
180 #[must_use]
181 pub fn consumer_client(&self) -> ConsumerClient<'_> {
182 ConsumerClient { api: self }
183 }
184}
185
186impl MQClientAPIImpl {
187 pub async fn notification(
188 &self,
189 broker_addr: &CheetahString,
190 request_header: NotificationRequestHeader,
191 timeout_millis: u64,
192 ) -> crate::ClientResult<bool> {
193 self.notification_with_polling_stats(broker_addr, request_header, timeout_millis)
194 .await
195 .map(|result| result.is_has_msg())
196 }
197
198 pub async fn notification_with_polling_stats(
199 &self,
200 broker_addr: &CheetahString,
201 request_header: NotificationRequestHeader,
202 timeout_millis: u64,
203 ) -> crate::ClientResult<NotifyResult> {
204 let request = notification_request(&self.command_factory, request_header);
205 let outcome = self
206 .remoting_client
207 .invoke_request(Some(broker_addr), request, timeout_millis)
208 .await;
209 let response = match outcome {
210 Ok(OutboundRequestOutcome::Response(response)) => response,
211 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
212 return Err(consumer_request_error(
213 "notification_with_polling_stats",
214 RetryInput::Rejected(rejection),
215 ));
216 }
217 Ok(OutboundRequestOutcome::Contract(contract)) => {
218 return Err(consumer_request_error(
219 "notification_with_polling_stats",
220 RetryInput::Contract(contract),
221 ));
222 }
223 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
224 };
225
226 match ResponseCode::from(response.code()) {
227 ResponseCode::Success => notify_result_from_response(&response),
228 _ => Err(client_broker_err!(
229 response.code(),
230 response.remark().map_or_else(String::new, |remark| remark.to_string()),
231 broker_addr
232 )),
233 }
234 }
235
236 pub async fn get_consumer_id_list_by_group(
237 &self,
238 addr: &str,
239 consumer_group: &str,
240 timeout_millis: u64,
241 ) -> crate::ClientResult<Vec<CheetahString>> {
242 let request_header = GetConsumerListByGroupRequestHeader {
243 consumer_group: CheetahString::from_slice(consumer_group),
244 rpc: None,
245 };
246 let request = self.create_request_command(RequestCode::GetConsumerListByGroup, request_header);
247 let outcome = self
248 .remoting_client
249 .invoke_request(
250 Some(mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref()),
251 request,
252 timeout_millis,
253 )
254 .await;
255 let response = match outcome {
256 Ok(OutboundRequestOutcome::Response(response)) => response,
257 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
258 return Err(consumer_request_error(
259 "get_consumer_id_list_by_group",
260 RetryInput::Rejected(rejection),
261 ));
262 }
263 Ok(OutboundRequestOutcome::Contract(contract)) => {
264 return Err(consumer_request_error(
265 "get_consumer_id_list_by_group",
266 RetryInput::Contract(contract),
267 ));
268 }
269 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
270 };
271 match ResponseCode::from(response.code()) {
272 ResponseCode::Success => {
273 let _body = response.body();
274 if let Some(body) = response.body() {
275 return match GetConsumerListByGroupResponseBody::decode(body) {
276 Ok(value) => Ok(value.consumer_id_list),
277 Err(_e) => Err(mq_client_err!(response
278 .remark()
279 .map_or("".to_string(), |s| s.to_string()))),
280 };
281 }
282 }
283 _ => {
284 return Err(client_broker_err!(
285 response.code(),
286 response.remark().map_or("".to_string(), |s| s.to_string()),
287 addr
288 ))
289 }
290 }
291 Err(client_broker_err!(
292 response.code(),
293 response.remark().map_or("".to_string(), |s| s.to_string()),
294 addr
295 ))
296 }
297
298 pub async fn get_consumer_connection_list(
299 &self,
300 addr: &str,
301 consumer_group: CheetahString,
302 timeout_millis: u64,
303 ) -> crate::ClientResult<rocketmq_protocol::protocol::body::consumer_connection::ConsumerConnection> {
304 let request_header = GetConsumerConnectionListRequestHeader {
305 consumer_group,
306 rpc_request_header: None,
307 };
308 let request = self.create_request_command(RequestCode::GetConsumerConnectionList, request_header);
309 let outcome = self
310 .remoting_client
311 .invoke_request(
312 Some(mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref()),
313 request,
314 timeout_millis,
315 )
316 .await;
317 let response = match outcome {
318 Ok(OutboundRequestOutcome::Response(response)) => response,
319 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
320 return Err(consumer_request_error(
321 "get_consumer_connection_list",
322 RetryInput::Rejected(rejection),
323 ));
324 }
325 Ok(OutboundRequestOutcome::Contract(contract)) => {
326 return Err(consumer_request_error(
327 "get_consumer_connection_list",
328 RetryInput::Contract(contract),
329 ));
330 }
331 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
332 };
333 match ResponseCode::from(response.code()) {
334 ResponseCode::Success => {
335 if let Some(body) = response.body() {
336 return ConsumerConnection::decode(body).map_err(ClientError::from);
337 }
338 }
339 _ => {
340 return Err(client_broker_err!(
341 response.code(),
342 response.remark().map_or("".to_string(), |s| s.to_string()),
343 addr
344 ))
345 }
346 }
347 Err(client_broker_err!(
348 response.code(),
349 response.remark().map_or("".to_string(), |s| s.to_string()),
350 addr
351 ))
352 }
353
354 pub async fn get_producer_connection_list(
355 &self,
356 addr: &str,
357 producer_group: CheetahString,
358 timeout_millis: u64,
359 ) -> crate::ClientResult<ProducerConnection> {
360 let request_header = GetProducerConnectionListRequestHeader {
361 producer_group,
362 rpc_request_header: None,
363 };
364 let request = self.create_request_command(RequestCode::GetProducerConnectionList, request_header);
365 let outcome = self
366 .remoting_client
367 .invoke_request(
368 Some(mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref()),
369 request,
370 timeout_millis,
371 )
372 .await;
373 let response = match outcome {
374 Ok(OutboundRequestOutcome::Response(response)) => response,
375 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
376 return Err(consumer_request_error(
377 "get_producer_connection_list",
378 RetryInput::Rejected(rejection),
379 ));
380 }
381 Ok(OutboundRequestOutcome::Contract(contract)) => {
382 return Err(consumer_request_error(
383 "get_producer_connection_list",
384 RetryInput::Contract(contract),
385 ));
386 }
387 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
388 };
389 match ResponseCode::from(response.code()) {
390 ResponseCode::Success => {
391 if let Some(body) = response.body() {
392 return ProducerConnection::decode(body).map_err(ClientError::from);
393 }
394 }
395 _ => {
396 return Err(client_broker_err!(
397 response.code(),
398 response.remark().map_or("".to_string(), |s| s.to_string()),
399 addr
400 ));
401 }
402 }
403 Err(client_broker_err!(
404 response.code(),
405 response.remark().map_or("".to_string(), |s| s.to_string()),
406 addr
407 ))
408 }
409
410 pub async fn invoke_broker_to_get_consumer_status(
411 &self,
412 addr: &str,
413 topic: CheetahString,
414 group: CheetahString,
415 client_addr: CheetahString,
416 timeout_millis: u64,
417 ) -> crate::ClientResult<HashMap<CheetahString, HashMap<MessageQueue, i64>>> {
418 let request_header = GetConsumerStatusRequestHeader {
419 topic,
420 group,
421 client_addr: if client_addr.is_empty() {
422 None
423 } else {
424 Some(client_addr)
425 },
426 topic_request_header: None,
427 };
428 let request = self.create_request_command(RequestCode::InvokeBrokerToGetConsumerStatus, request_header);
429 let outcome = self
430 .remoting_client
431 .invoke_request(
432 Some(mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref()),
433 request,
434 timeout_millis,
435 )
436 .await;
437 let response = match outcome {
438 Ok(OutboundRequestOutcome::Response(response)) => response,
439 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
440 return Err(consumer_request_error(
441 "invoke_broker_to_get_consumer_status",
442 RetryInput::Rejected(rejection),
443 ));
444 }
445 Ok(OutboundRequestOutcome::Contract(contract)) => {
446 return Err(consumer_request_error(
447 "invoke_broker_to_get_consumer_status",
448 RetryInput::Contract(contract),
449 ));
450 }
451 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
452 };
453 match ResponseCode::from(response.code()) {
454 ResponseCode::Success => {
455 if let Some(body) = response.body() {
456 if let Some(status_body) = GetConsumerStatusBody::decode(body) {
457 return Ok(status_body.consumer_table);
458 }
459 }
460 Ok(HashMap::new())
461 }
462 _ => Err(mq_client_err!(
463 response.code(),
464 format!(
465 "invoke broker to get consumer status failed, remark={}",
466 response.remark().map_or("".to_string(), |s| s.to_string()),
467 )
468 )),
469 }
470 }
471
472 pub async fn get_all_producer_info(
473 &self,
474 addr: &str,
475 timeout_millis: u64,
476 ) -> crate::ClientResult<ProducerTableInfo> {
477 let request = self.create_request_command(RequestCode::GetAllProducerInfo, EmptyHeader {});
478 let outcome = self
479 .remoting_client
480 .invoke_request(
481 Some(mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref()),
482 request,
483 timeout_millis,
484 )
485 .await;
486 let response = match outcome {
487 Ok(OutboundRequestOutcome::Response(response)) => response,
488 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
489 return Err(consumer_request_error(
490 "get_all_producer_info",
491 RetryInput::Rejected(rejection),
492 ));
493 }
494 Ok(OutboundRequestOutcome::Contract(contract)) => {
495 return Err(consumer_request_error(
496 "get_all_producer_info",
497 RetryInput::Contract(contract),
498 ));
499 }
500 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
501 };
502 match ResponseCode::from(response.code()) {
503 ResponseCode::Success => {
504 if let Some(body) = response.body() {
505 return ProducerTableInfo::decode(body).map_err(ClientError::from);
506 }
507 }
508 _ => {
509 return Err(client_broker_err!(
510 response.code(),
511 response.remark().map_or("".to_string(), |s| s.to_string()),
512 addr
513 ));
514 }
515 }
516 Err(client_broker_err!(
517 response.code(),
518 response.remark().map_or("".to_string(), |s| s.to_string()),
519 addr
520 ))
521 }
522
523 pub async fn update_consumer_offset_oneway(
524 &self,
525 addr: &str,
526 request_header: UpdateConsumerOffsetRequestHeader,
527 timeout_millis: u64,
528 ) -> crate::ClientResult<()> {
529 let request = self.create_request_command(RequestCode::UpdateConsumerOffset, request_header);
530 self.remoting_client
531 .invoke_request_oneway(
532 mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref(),
533 request,
534 timeout_millis,
535 )
536 .await
537 .map_err(ClientError::from)
538 }
539
540 pub async fn update_consumer_offset_one_way(
541 &self,
542 addr: &str,
543 request_header: UpdateConsumerOffsetRequestHeader,
544 timeout_millis: u64,
545 ) -> crate::ClientResult<()> {
546 self.update_consumer_offset_oneway(addr, request_header, timeout_millis)
547 .await
548 }
549
550 pub async fn update_consumer_offset(
551 &self,
552 addr: &CheetahString,
553 request_header: UpdateConsumerOffsetRequestHeader,
554 timeout_millis: u64,
555 ) -> crate::ClientResult<()> {
556 let request = self.create_request_command(RequestCode::UpdateConsumerOffset, request_header);
557 let outcome = self
558 .remoting_client
559 .invoke_request(Some(addr), request, timeout_millis)
560 .await;
561 let response = match outcome {
562 Ok(OutboundRequestOutcome::Response(response)) => response,
563 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
564 return Err(consumer_request_error(
565 "update_consumer_offset",
566 RetryInput::Rejected(rejection),
567 ));
568 }
569 Ok(OutboundRequestOutcome::Contract(contract)) => {
570 return Err(consumer_request_error(
571 "update_consumer_offset",
572 RetryInput::Contract(contract),
573 ));
574 }
575 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
576 };
577 if ResponseCode::from(response.code()) != ResponseCode::Success {
578 Err(client_broker_err!(
579 response.code(),
580 response.remark().map_or("".to_string(), |s| s.to_string()),
581 addr
582 ))
583 } else {
584 Ok(())
585 }
586 }
587
588 pub async fn update_consumer_offset_async(
589 &self,
590 addr: &CheetahString,
591 request_header: UpdateConsumerOffsetRequestHeader,
592 timeout_millis: u64,
593 ) -> crate::ClientResult<()> {
594 self.update_consumer_offset(addr, request_header, timeout_millis).await
595 }
596
597 pub async fn query_consumer_offset(
598 &self,
599 addr: &str,
600 request_header: QueryConsumerOffsetRequestHeader,
601 timeout_millis: u64,
602 ) -> crate::ClientResult<i64> {
603 let request = self.create_request_command(RequestCode::QueryConsumerOffset, request_header);
604 let outcome = self
605 .remoting_client
606 .invoke_request(
607 Some(mix_all::broker_vip_channel(self.client_config.vip_channel_enabled, addr).as_ref()),
608 request,
609 timeout_millis,
610 )
611 .await;
612 let response = match outcome {
613 Ok(OutboundRequestOutcome::Response(response)) => response,
614 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
615 return Err(consumer_request_error(
616 "query_consumer_offset",
617 RetryInput::Rejected(rejection),
618 ));
619 }
620 Ok(OutboundRequestOutcome::Contract(contract)) => {
621 return Err(consumer_request_error(
622 "query_consumer_offset",
623 RetryInput::Contract(contract),
624 ));
625 }
626 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
627 };
628 match ResponseCode::from(response.code()) {
629 ResponseCode::Success => {
630 let response_header = response.decode_command_custom_header::<QueryConsumerOffsetResponseHeader>()?;
631 return response_header.offset.ok_or_else(|| {
632 client_broker_err!(
633 response.code(),
634 "QueryConsumerOffset response header missing offset",
635 addr
636 )
637 });
638 }
639 ResponseCode::QueryNotFound => {
640 return Err(client_broker_err!(
641 response.code(),
642 response.remark().map_or("".to_string(), |s| s.to_string()),
643 addr
644 ));
645 }
646 _ => {}
647 }
648 Err(client_broker_err!(
649 response.code(),
650 response.remark().map_or("".to_string(), |s| s.to_string()),
651 addr
652 ))
653 }
654
655 pub async fn query_consumer_offset_with_future(
656 &self,
657 addr: &str,
658 request_header: QueryConsumerOffsetRequestHeader,
659 timeout_millis: u64,
660 ) -> crate::ClientResult<i64> {
661 self.query_consumer_offset(addr, request_header, timeout_millis).await
662 }
663
664 pub async fn query_message(
665 this: &Arc<Self>,
666 addr: &CheetahString,
667 request_header: QueryMessageRequestHeader,
668 unique_key_flag: bool,
669 timeout_millis: u64,
670 ) -> crate::ClientResult<Option<(QueryMessageResponseHeader, Option<bytes::Bytes>)>> {
671 let mut request = this.create_request_command(RequestCode::QueryMessage, request_header);
672 if unique_key_flag {
673 request.ensure_ext_fields_initialized();
674 request.add_ext_field(mix_all::UNIQUE_MSG_QUERY_FLAG, "true");
675 }
676 let outcome = this
677 .remoting_client
678 .invoke_request(Some(addr), request, timeout_millis)
679 .await;
680 let response = match outcome {
681 Ok(OutboundRequestOutcome::Response(response)) => response,
682 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
683 return Err(consumer_request_error("query_message", RetryInput::Rejected(rejection)));
684 }
685 Ok(OutboundRequestOutcome::Contract(contract)) => {
686 return Err(consumer_request_error("query_message", RetryInput::Contract(contract)));
687 }
688 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
689 };
690 match ResponseCode::from(response.code()) {
691 ResponseCode::Success => {
692 let response_header = response
693 .decode_command_custom_header::<QueryMessageResponseHeader>()
694 .map_err(|e| ClientError::response_process_source("decode QueryMessageResponseHeader", e))?;
695 let body = response.body().cloned();
696 Ok(Some((response_header, body)))
697 }
698 ResponseCode::QueryNotFound => Ok(None),
699 _ => Err(client_broker_err!(
700 response.code(),
701 response.remark().map_or("".to_string(), |s| s.to_string()),
702 addr
703 )),
704 }
705 }
706
707 pub async fn pull_message<PCB>(
708 this: Arc<Self>,
709 addr: CheetahString,
710 request_header: PullMessageRequestHeader,
711 timeout_millis: u64,
712 communication_mode: CommunicationMode,
713 pull_callback: PCB,
714 ) -> crate::ClientResult<Option<PullResultExt>>
715 where
716 PCB: PullCallback + 'static,
717 {
718 let request = if PullSysFlag::has_lite_pull_flag(request_header.sys_flag as u32) {
719 this.create_request_command(RequestCode::LitePullMessage, request_header)
720 } else {
721 this.create_request_command(RequestCode::PullMessage, request_header)
722 };
723 match communication_mode {
724 CommunicationMode::Sync => {
725 let result_ext = this.pull_message_sync(&addr, request, timeout_millis).await?;
726 Ok(Some(result_ext))
727 }
728 CommunicationMode::Async => {
729 let tracker = this.background_tasks.clone();
730 let shutdown_token = this.background_shutdown.clone();
731 let service_context = this.service_context.clone();
732 Self::spawn_api_background_task(
733 &service_context,
734 "rocketmq-client-pull-message-async",
735 &tracker,
736 &shutdown_token,
737 async move {
738 let _ = this
739 .pull_message_async(&addr, request, timeout_millis, pull_callback)
740 .await;
741 },
742 );
743 Ok(None)
744 }
745 CommunicationMode::Oneway => Ok(None),
746 }
747 }
748
749 pub(super) async fn pull_message_sync(
750 &self,
751 addr: &CheetahString,
752 request: RemotingCommand,
753 timeout_millis: u64,
754 ) -> crate::ClientResult<PullResultExt> {
755 let outcome = self
756 .remoting_client
757 .invoke_request(Some(addr), request, timeout_millis)
758 .await;
759 let response = match outcome {
760 Ok(OutboundRequestOutcome::Response(response)) => response,
761 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
762 return Err(consumer_request_error(
763 "pull_message_sync",
764 RetryInput::Rejected(rejection),
765 ));
766 }
767 Ok(OutboundRequestOutcome::Contract(contract)) => {
768 return Err(consumer_request_error(
769 "pull_message_sync",
770 RetryInput::Contract(contract),
771 ));
772 }
773 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
774 };
775 self.process_pull_response(response, addr).await
776 }
777
778 pub(super) async fn pull_message_async<PCB>(
779 &self,
780 addr: &CheetahString,
781 request: RemotingCommand,
782 timeout_millis: u64,
783 mut pull_callback: PCB,
784 ) -> crate::ClientResult<()>
785 where
786 PCB: PullCallback,
787 {
788 match self
789 .remoting_client
790 .invoke_request(Some(addr), request, timeout_millis)
791 .await
792 {
793 Ok(OutboundRequestOutcome::Response(response)) => {
794 let result = self.process_pull_response(response, addr).await;
795 let _ = self
796 .callback_executor
797 .execute(async {
798 match result {
799 Ok(pull_result) => pull_callback.on_success(pull_result).await,
800 Err(error) => pull_callback.on_exception(error),
801 }
802 })
803 .await;
804 }
805 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
806 let error = consumer_request_error("pull_message_async", RetryInput::Rejected(rejection));
807 let _ = self
808 .callback_executor
809 .execute(async { pull_callback.on_exception(error) })
810 .await;
811 }
812 Ok(OutboundRequestOutcome::Contract(contract)) => {
813 let error = consumer_request_error("pull_message_async", RetryInput::Contract(contract));
814 let _ = self
815 .callback_executor
816 .execute(async { pull_callback.on_exception(error) })
817 .await;
818 }
819 Err(error) => {
820 let error = ClientError::from_shared(error.into_shared_error());
821 let _ = self
822 .callback_executor
823 .execute(async { pull_callback.on_exception(error) })
824 .await;
825 }
826 }
827 Ok(())
828 }
829
830 pub(super) async fn process_pull_response(
831 &self,
832 mut response: RemotingCommand,
833 addr: &CheetahString,
834 ) -> crate::ClientResult<PullResultExt> {
835 let pull_status = match ResponseCode::from(response.code()) {
836 ResponseCode::Success => PullStatus::Found,
837 ResponseCode::PullNotFound => PullStatus::NoNewMsg,
838 ResponseCode::PullRetryImmediately => PullStatus::NoMatchedMsg,
839 ResponseCode::PullOffsetMoved => PullStatus::OffsetIllegal,
840 _ => {
841 return Err(client_broker_err!(
842 response.code(),
843 response.remark().map_or("".to_string(), |s| s.to_string()),
844 addr
845 ));
846 }
847 };
848 let response_header = response.decode_command_custom_header::<PullMessageResponseHeader>()?;
849 let next_begin_offset =
850 java_long_to_u64_field("pullMessage", "nextBeginOffset", response_header.next_begin_offset)?;
851 let min_offset = java_long_to_u64_field("pullMessage", "minOffset", response_header.min_offset)?;
852 let max_offset = java_long_to_u64_field("pullMessage", "maxOffset", response_header.max_offset)?;
853 let pull_result = PullResultExt {
854 pull_result: PullResult {
855 pull_status,
856 next_begin_offset,
857 min_offset,
858 max_offset,
859 msg_found_list: Some(vec![]),
860 },
861 suggest_which_broker_id: response_header.suggest_which_broker_id,
862 message_binary: response.take_body(),
863 offset_delta: response_header.offset_delta,
864 };
865 Ok(pull_result)
866 }
867
868 pub async fn consumer_send_message_back(
869 &self,
870 addr: &str,
871 broker_name: Option<&str>,
872 msg: &MessageExt,
873 consumer_group: &str,
874 delay_level: i32,
875 timeout_millis: u64,
876 max_consume_retry_times: i32,
877 ) -> crate::ClientResult<()> {
878 let header = Self::consumer_send_message_back_request_header(
879 msg,
880 broker_name,
881 consumer_group,
882 delay_level,
883 max_consume_retry_times,
884 );
885
886 let request_command = self.create_request_command(RequestCode::ConsumerSendMsgBack, header);
887 let outcome = self
888 .remoting_client
889 .invoke_request(
890 Some(&mix_all::broker_vip_channel(
891 self.client_config.vip_channel_enabled,
892 addr,
893 )),
894 request_command,
895 timeout_millis,
896 )
897 .await;
898 let response = match outcome {
899 Ok(OutboundRequestOutcome::Response(response)) => response,
900 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
901 return Err(consumer_request_error(
902 "consumer_send_message_back",
903 RetryInput::Rejected(rejection),
904 ));
905 }
906 Ok(OutboundRequestOutcome::Contract(contract)) => {
907 return Err(consumer_request_error(
908 "consumer_send_message_back",
909 RetryInput::Contract(contract),
910 ));
911 }
912 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
913 };
914 if ResponseCode::from(response.code()) == ResponseCode::Success {
915 Ok(())
916 } else {
917 Err(client_broker_err!(
918 response.code(),
919 response.remark().map_or("".to_string(), |s| s.to_string()),
920 addr
921 ))
922 }
923 }
924
925 pub(super) fn consumer_send_message_back_request_header(
926 msg: &MessageExt,
927 broker_name: Option<&str>,
928 consumer_group: &str,
929 delay_level: i32,
930 max_consume_retry_times: i32,
931 ) -> ConsumerSendMsgBackRequestHeader {
932 ConsumerSendMsgBackRequestHeader {
933 offset: msg.commit_log_offset,
934 group: CheetahString::from_slice(consumer_group),
935 delay_level,
936 origin_msg_id: Some(CheetahString::from_slice(msg.msg_id.as_str())),
937 origin_topic: Some(CheetahString::from_slice(msg.topic())),
938 unit_mode: false,
939 max_reconsume_times: Some(max_consume_retry_times),
940 rpc_request_header: Some(RpcRequestHeader {
941 namespace: None,
942 namespaced: None,
943 broker_name: broker_name.map(CheetahString::from_slice),
944 oneway: None,
945 }),
946 }
947 }
948
949 pub async fn send_message_back_async(
950 &self,
951 addr: &CheetahString,
952 request_header: ConsumerSendMsgBackRequestHeader,
953 timeout_millis: u64,
954 ) -> crate::ClientResult<RemotingCommand> {
955 let request = self.create_request_command(RequestCode::ConsumerSendMsgBack, request_header);
956 let outcome = self
957 .remoting_client
958 .invoke_request(Some(addr), request, timeout_millis)
959 .await;
960 match outcome {
961 Ok(OutboundRequestOutcome::Response(response)) => Ok(response),
962 Ok(OutboundRequestOutcome::Rejected(rejection)) => Err(consumer_request_error(
963 "send_message_back_async",
964 RetryInput::Rejected(rejection),
965 )),
966 Ok(OutboundRequestOutcome::Contract(contract)) => Err(consumer_request_error(
967 "send_message_back_async",
968 RetryInput::Contract(contract),
969 )),
970 Err(error) => Err(ClientError::from_shared(error.into_shared_error())),
971 }
972 }
973
974 pub async fn unregister_client(
975 &self,
976 addr: &CheetahString,
977 client_id: CheetahString,
978 producer_group: Option<CheetahString>,
979 consumer_group: Option<CheetahString>,
980 timeout_millis: u64,
981 ) -> crate::ClientResult<()> {
982 let request_header = UnregisterClientRequestHeader {
983 client_id,
984 producer_group,
985 consumer_group,
986 rpc_request_header: None,
987 };
988 let request = self.create_request_command(RequestCode::UnregisterClient, request_header);
989 let outcome = self
990 .remoting_client
991 .invoke_request(Some(addr), request, timeout_millis)
992 .await;
993 let response = match outcome {
994 Ok(OutboundRequestOutcome::Response(response)) => response,
995 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
996 return Err(consumer_request_error(
997 "unregister_client",
998 RetryInput::Rejected(rejection),
999 ));
1000 }
1001 Ok(OutboundRequestOutcome::Contract(contract)) => {
1002 return Err(consumer_request_error(
1003 "unregister_client",
1004 RetryInput::Contract(contract),
1005 ));
1006 }
1007 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
1008 };
1009 if ResponseCode::from(response.code()) == ResponseCode::Success {
1010 Ok(())
1011 } else {
1012 Err(client_broker_err!(
1013 response.code(),
1014 response.remark().map_or("".to_string(), |s| s.to_string()),
1015 addr
1016 ))
1017 }
1018 }
1019
1020 pub async fn unlock_batch_mq(
1021 &self,
1022 addr: &CheetahString,
1023 request_body: UnlockBatchRequestBody,
1024 timeout_millis: u64,
1025 oneway: bool,
1026 ) -> crate::ClientResult<()> {
1027 let mut request =
1028 self.create_request_command(RequestCode::UnlockBatchMq, UnlockBatchMqRequestHeader::default());
1029 request.set_body_mut_ref(request_body.encode()?);
1030 if oneway {
1031 self.remoting_client
1032 .invoke_request_oneway(addr, request, timeout_millis)
1033 .await
1034 .map_err(ClientError::from)
1035 } else {
1036 let outcome = self
1037 .remoting_client
1038 .invoke_request(
1039 Some(&mix_all::broker_vip_channel(
1040 self.client_config.vip_channel_enabled,
1041 addr,
1042 )),
1043 request,
1044 timeout_millis,
1045 )
1046 .await;
1047 let response = match outcome {
1048 Ok(OutboundRequestOutcome::Response(response)) => response,
1049 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
1050 return Err(consumer_request_error(
1051 "unlock_batch_mq",
1052 RetryInput::Rejected(rejection),
1053 ));
1054 }
1055 Ok(OutboundRequestOutcome::Contract(contract)) => {
1056 return Err(consumer_request_error(
1057 "unlock_batch_mq",
1058 RetryInput::Contract(contract),
1059 ));
1060 }
1061 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
1062 };
1063 if ResponseCode::from(response.code()) == ResponseCode::Success {
1064 Ok(())
1065 } else {
1066 Err(client_broker_err!(
1067 response.code(),
1068 response.remark().map_or("".to_string(), |s| s.to_string()),
1069 addr
1070 ))
1071 }
1072 }
1073 }
1074
1075 pub async fn unlock_batch_mq_oneway(
1076 &self,
1077 addr: &CheetahString,
1078 request_body: UnlockBatchRequestBody,
1079 timeout_millis: u64,
1080 ) -> crate::ClientResult<()> {
1081 self.unlock_batch_mq(addr, request_body, timeout_millis, true).await
1082 }
1083
1084 pub async fn lock_batch_mq(
1085 &self,
1086 addr: &str,
1087 request_body: LockBatchRequestBody,
1088 timeout_millis: u64,
1089 ) -> crate::ClientResult<HashSet<MessageQueue>> {
1090 let mut request = self.create_request_command(RequestCode::LockBatchMq, LockBatchMqRequestHeader::default());
1091 request.set_body_mut_ref(request_body.encode()?);
1092 let outcome = self
1093 .remoting_client
1094 .invoke_request(
1095 Some(&mix_all::broker_vip_channel(
1096 self.client_config.vip_channel_enabled,
1097 addr,
1098 )),
1099 request,
1100 timeout_millis,
1101 )
1102 .await;
1103 let response = match outcome {
1104 Ok(OutboundRequestOutcome::Response(response)) => response,
1105 Ok(OutboundRequestOutcome::Rejected(rejection)) => {
1106 return Err(consumer_request_error("lock_batch_mq", RetryInput::Rejected(rejection)));
1107 }
1108 Ok(OutboundRequestOutcome::Contract(contract)) => {
1109 return Err(consumer_request_error("lock_batch_mq", RetryInput::Contract(contract)));
1110 }
1111 Err(error) => return Err(ClientError::from_shared(error.into_shared_error())),
1112 };
1113 if ResponseCode::from(response.code()) == ResponseCode::Success {
1114 if let Some(body) = response.body() {
1115 LockBatchResponseBody::decode(body.as_ref())
1116 .map(|body| body.lock_ok_mq_set)
1117 .map_err(|source| {
1118 ClientError::broker_operation_source("lock_batch_mq", response.code(), Some(addr), source)
1119 })
1120 } else {
1121 Err(client_broker_err!(response.code(), "Response body is empty", addr))
1122 }
1123 } else {
1124 Err(client_broker_err!(
1125 response.code(),
1126 response.remark().map_or("".to_string(), |s| s.to_string()),
1127 addr
1128 ))
1129 }
1130 }
1131
1132 pub async fn lock_batch_mq_with_future(
1133 &self,
1134 addr: &str,
1135 request_body: LockBatchRequestBody,
1136 timeout_millis: u64,
1137 ) -> crate::ClientResult<HashSet<MessageQueue>> {
1138 self.lock_batch_mq(addr, request_body, timeout_millis).await
1139 }
1140}
1141
1142mod message_operations;
1143
1144#[cfg(test)]
1145mod tests {
1146 use super::*;
1147
1148 #[test]
1149 fn pop_background_task_spawn_failure_preserves_cause() {
1150 let error = pop_background_task_spawn_failed(std::io::Error::other("task group closed"));
1151
1152 assert_eq!(
1153 error.descriptor().code(),
1154 rocketmq_error::CLIENT_LIFECYCLE_INVALID_STATE.code()
1155 );
1156 assert!(error.source_ref::<std::io::Error>().is_some());
1157 }
1158}