rocketmq_client_rust/consumer/consumer_impl/
pull_message_service.rs1use std::future::Future;
16use std::sync::atomic::AtomicBool;
17use std::sync::atomic::Ordering;
18use std::sync::Arc;
19use std::thread;
20use std::time::Duration;
21
22use rocketmq_common::common::message::message_enum::MessageRequestMode;
23use rocketmq_error::RocketMQError;
24use rocketmq_rust::ArcMut;
25use rocketmq_rust::Shutdown;
26use tracing::error;
27use tracing::info;
28use tracing::warn;
29
30use crate::consumer::consumer_impl::message_request::MessageRequest;
31use crate::consumer::consumer_impl::pop_request::PopRequest;
32use crate::consumer::consumer_impl::pull_request::PullRequest;
33use crate::factory::mq_client_instance::MQClientInstance;
34
35const DEFAULT_QUEUE_CAPACITY: usize = 4096;
37
38const DEFAULT_SHUTDOWN_TIMEOUT_MS: u64 = 1000;
40
41#[derive(Clone)]
57pub struct PullMessageService {
58 tx: Option<tokio::sync::mpsc::Sender<Box<dyn MessageRequest + Send + 'static>>>,
60
61 tx_shutdown: Option<tokio::sync::broadcast::Sender<()>>,
63
64 stopped: Arc<AtomicBool>,
66
67 queue_capacity: usize,
69}
70
71impl PullMessageService {
72 pub fn new() -> Self {
74 Self::with_capacity(DEFAULT_QUEUE_CAPACITY)
75 }
76
77 pub fn with_capacity(queue_capacity: usize) -> Self {
79 PullMessageService {
80 tx: None,
81 tx_shutdown: None,
82 stopped: Arc::new(AtomicBool::new(false)),
83 queue_capacity,
84 }
85 }
86
87 #[inline]
89 pub fn is_stopped(&self) -> bool {
90 self.stopped.load(Ordering::Acquire)
91 }
92
93 #[inline]
95 pub fn get_service_name(&self) -> &'static str {
96 "PullMessageService"
97 }
98
99 pub async fn start(&mut self, mut instance: ArcMut<MQClientInstance>) -> Result<(), RocketMQError> {
107 if self.tx.is_some() {
108 warn!("{} already started", self.get_service_name());
109 return Ok(());
110 }
111
112 let (tx, mut rx) = tokio::sync::mpsc::channel::<Box<dyn MessageRequest + Send + 'static>>(self.queue_capacity);
113 let (mut shutdown, tx_shutdown) = Shutdown::new(1);
114
115 self.tx = Some(tx);
116 self.tx_shutdown = Some(tx_shutdown);
117 self.stopped.store(false, Ordering::Release);
118
119 spawn_pull_message_task("rocketmq-client-pull-message-service", async move {
120 info!("{} service started", "PullMessageService");
121
122 loop {
123 tokio::select! {
124 _ = shutdown.recv() => {
125 info!("{} received shutdown signal", "PullMessageService");
126 break;
127 }
128 Some(request) = rx.recv() => {
129 if let Err(e) = Self::process_request(request, &mut instance).await {
131 error!("{} failed to process request: {:?}", "PullMessageService", e);
132 }
133 }
134 }
135 }
136
137 info!("{} service end", "PullMessageService");
138 });
139
140 Ok(())
141 }
142
143 async fn process_request(
152 request: Box<dyn MessageRequest + Send + 'static>,
153 instance: &mut MQClientInstance,
154 ) -> Result<(), RocketMQError> {
155 match request.get_message_request_mode() {
156 MessageRequestMode::Pull => {
157 if let Ok(pull_request) = request.into_any().downcast::<PullRequest>() {
159 Self::pull_message(*pull_request, instance).await;
160 Ok(())
161 } else {
162 Err(RocketMQError::Internal("Failed to downcast to PullRequest".to_string()))
163 }
164 }
165 MessageRequestMode::Pop => {
166 if let Ok(pop_request) = request.into_any().downcast::<PopRequest>() {
167 Self::pop_message(*pop_request, instance).await;
168 Ok(())
169 } else {
170 Err(RocketMQError::Internal("Failed to downcast to PopRequest".to_string()))
171 }
172 }
173 }
174 }
175
176 async fn pull_message(request: PullRequest, instance: &mut MQClientInstance) {
178 if let Some(mut consumer) = instance.select_consumer(request.get_consumer_group()).await {
179 consumer.pull_message(request).await;
180 } else {
181 warn!("No matched consumer for the PullRequest {}, drop it", request)
182 }
183 }
184
185 async fn pop_message(request: PopRequest, instance: &mut MQClientInstance) {
187 if let Some(mut consumer) = instance.select_consumer(request.get_consumer_group()).await {
188 consumer.pop_message(request).await;
189 } else {
190 warn!("No matched consumer for the PopRequest {}, drop it", request)
191 }
192 }
193
194 pub fn execute_pull_request_later(&self, pull_request: PullRequest, time_delay: u64) {
204 if self.is_stopped() {
205 warn!("{} has shutdown, cannot execute later task", self.get_service_name());
206 return;
207 }
208
209 let this = self.clone();
210 let request = pull_request.clone();
211
212 spawn_pull_message_task("rocketmq-client-pull-request-delay", async move {
214 tokio::time::sleep(Duration::from_millis(time_delay)).await;
215
216 if this.is_stopped() {
217 return;
218 }
219
220 if let Some(tx) = &this.tx {
221 if let Err(e) = tx.send(Box::new(request)).await {
222 warn!("Failed to send pull request: {:?}", e);
223 }
224 }
225 });
226 }
227
228 pub async fn execute_pull_request_immediately(&self, pull_request: PullRequest) {
236 if self.is_stopped() {
237 warn!("PullMessageService has shutdown");
238 return;
239 }
240
241 if let Some(tx) = &self.tx {
242 if let Err(e) = tx.send(Box::new(pull_request)).await {
243 error!("executePullRequestImmediately messageRequestQueue.put error: {:?}", e);
244 }
245 } else {
246 warn!("PullMessageService not started");
247 }
248 }
249
250 pub fn execute_pop_pull_request_later(&self, pop_request: PopRequest, time_delay: u64) {
252 if self.is_stopped() {
253 warn!("{} has shutdown, cannot execute later task", self.get_service_name());
254 return;
255 }
256
257 let this = self.clone();
258 let request = pop_request.clone();
259
260 spawn_pull_message_task("rocketmq-client-pop-request-delay", async move {
262 tokio::time::sleep(Duration::from_millis(time_delay)).await;
263
264 if this.is_stopped() {
265 return;
266 }
267
268 if let Some(tx) = &this.tx {
269 if let Err(e) = tx.send(Box::new(request)).await {
270 warn!("Failed to send pop request: {:?}", e);
271 }
272 }
273 });
274 }
275
276 pub async fn execute_pop_pull_request_immediately(&self, pop_request: PopRequest) {
278 if self.is_stopped() {
279 warn!("PullMessageService has shutdown");
280 return;
281 }
282
283 if let Some(tx) = &self.tx {
284 if let Err(e) = tx.send(Box::new(pop_request)).await {
285 error!(
286 "executePopPullRequestImmediately messageRequestQueue.put error: {:?}",
287 e
288 );
289 }
290 } else {
291 warn!("PullMessageService not started");
292 }
293 }
294
295 pub fn execute_task_later<F>(&self, task: F, time_delay: u64)
301 where
302 F: FnOnce() + Send + 'static,
303 {
304 if self.is_stopped() {
305 warn!("{} has shutdown, cannot execute task", self.get_service_name());
306 return;
307 }
308
309 let stopped = self.stopped.clone();
310 spawn_pull_message_task("rocketmq-client-pull-task-delay", async move {
311 tokio::time::sleep(Duration::from_millis(time_delay)).await;
312 if stopped.load(Ordering::Acquire) {
313 return;
314 }
315 task();
316 });
317 }
318
319 pub fn execute_task<F>(&self, task: F)
321 where
322 F: FnOnce() + Send + 'static,
323 {
324 if self.is_stopped() {
325 warn!("{} has shutdown, cannot execute task", self.get_service_name());
326 return;
327 }
328
329 spawn_pull_message_task("rocketmq-client-pull-task", async move {
330 task();
331 });
332 }
333
334 pub async fn shutdown(&self, timeout_ms: u64) -> Result<(), RocketMQError> {
345 if self.is_stopped() {
346 warn!("{} already stopped", self.get_service_name());
347 return Ok(());
348 }
349
350 info!("{} shutting down...", self.get_service_name());
351
352 self.stopped.store(true, Ordering::Release);
354
355 if let Some(tx_shutdown) = &self.tx_shutdown {
357 tx_shutdown
358 .send(())
359 .map_err(|_| RocketMQError::Internal("Failed to send shutdown signal".to_string()))?;
360 }
361
362 let wait_result = tokio::time::timeout(Duration::from_millis(timeout_ms), async {
364 while self.tx.as_ref().map(|tx| !tx.is_closed()).unwrap_or(false) {
366 tokio::time::sleep(Duration::from_millis(10)).await;
367 }
368 })
369 .await;
370
371 if wait_result.is_err() {
372 warn!(
373 "{} shutdown timeout after {}ms, forcing exit",
374 self.get_service_name(),
375 timeout_ms
376 );
377 }
378
379 info!("{} shutdown completed", self.get_service_name());
380 Ok(())
381 }
382
383 pub async fn shutdown_default(&self) -> Result<(), RocketMQError> {
385 self.shutdown(DEFAULT_SHUTDOWN_TIMEOUT_MS).await
386 }
387}
388
389impl Default for PullMessageService {
390 fn default() -> Self {
391 Self::new()
392 }
393}
394
395fn spawn_pull_message_task<F>(thread_name: &'static str, task: F)
396where
397 F: Future<Output = ()> + Send + 'static,
398{
399 if let Ok(handle) = tokio::runtime::Handle::try_current() {
400 drop(handle.spawn(task));
401 return;
402 }
403
404 match thread::Builder::new().name(thread_name.to_string()).spawn(move || {
405 match tokio::runtime::Builder::new_current_thread().enable_all().build() {
406 Ok(runtime) => runtime.block_on(task),
407 Err(error) => error!("Failed to build {} runtime: {}", thread_name, error),
408 }
409 }) {
410 Ok(_handle) => {}
411 Err(error) => error!("Failed to spawn {} background thread: {}", thread_name, error),
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 #[test]
420 fn execute_task_without_tokio_runtime_does_not_spawn_panic() {
421 let service = PullMessageService::new();
422 let ran = Arc::new(AtomicBool::new(false));
423 let ran_clone = ran.clone();
424
425 service.execute_task(move || ran_clone.store(true, Ordering::Release));
426
427 std::thread::sleep(Duration::from_millis(50));
428 assert!(ran.load(Ordering::Acquire));
429 }
430
431 #[test]
432 fn execute_task_later_without_tokio_runtime_does_not_spawn_panic() {
433 let service = PullMessageService::new();
434 let ran = Arc::new(AtomicBool::new(false));
435 let ran_clone = ran.clone();
436
437 service.execute_task_later(move || ran_clone.store(true, Ordering::Release), 1);
438
439 std::thread::sleep(Duration::from_millis(50));
440 assert!(ran.load(Ordering::Acquire));
441 }
442}