Skip to main content

sz_orm_queue/
queue.rs

1use crate::error::MqError;
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, VecDeque};
5use std::sync::Arc;
6use tokio::sync::RwLock;
7
8#[async_trait]
9pub trait MessageQueue: Send + Sync {
10    async fn publish(&self, topic: &str, message: &[u8]) -> Result<(), MqError>;
11    async fn consume(&self, topic: &str) -> Result<Option<Message>, MqError>;
12    async fn ack(&self, message_id: &str) -> Result<(), MqError>;
13    async fn subscribe(&self, topic: &str) -> Result<(), MqError>;
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Message {
18    pub topic: String,
19    pub payload: Vec<u8>,
20    pub key: Option<String>,
21    pub timestamp: i64,
22    pub headers: HashMap<String, String>,
23    #[serde(default)]
24    pub id: String,
25}
26
27impl Message {
28    pub fn new(topic: impl Into<String>, payload: Vec<u8>) -> Self {
29        Self {
30            topic: topic.into(),
31            payload,
32            key: None,
33            timestamp: current_timestamp(),
34            headers: HashMap::new(),
35            id: String::new(),
36        }
37    }
38
39    pub fn with_key(mut self, key: impl Into<String>) -> Self {
40        self.key = Some(key.into());
41        self
42    }
43
44    pub fn text(&self) -> Option<&str> {
45        std::str::from_utf8(&self.payload).ok()
46    }
47
48    pub fn json<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
49        serde_json::from_slice(&self.payload).ok()
50    }
51
52    pub fn text_message(topic: impl Into<String>, text: impl Into<String>) -> Self {
53        Self::new(topic, text.into().into_bytes())
54    }
55
56    pub fn json_message<T: serde::Serialize>(
57        topic: impl Into<String>,
58        data: &T,
59    ) -> Result<Self, MqError> {
60        let payload = serde_json::to_vec(data)?;
61        Ok(Self::new(topic, payload))
62    }
63}
64
65/// 当前时间戳(毫秒)
66///
67/// M-10 修复:使用 `unwrap_or_default()` 会在系统时间早于 UNIX_EPOCH 时返回 0,
68/// 隐藏了潜在的时钟回拨问题。改为显式 match 并通过 eprintln! 记录事件,
69/// 便于在生产环境中排查(可被 stderr 重定向到日志收集系统)。
70fn current_timestamp() -> i64 {
71    use std::time::{SystemTime, UNIX_EPOCH};
72    match SystemTime::now().duration_since(UNIX_EPOCH) {
73        Ok(d) => d.as_millis() as i64,
74        Err(e) => {
75            // 系统时间早于 UNIX_EPOCH(时钟回拨或系统错误)
76            // 返回 0 作为兜底,避免 panic;生产环境应监控此事件
77            eprintln!(
78                "WARN: current_timestamp: system time before UNIX_EPOCH: {} (duration_secs={})",
79                e,
80                e.duration().as_secs()
81            );
82            0
83        }
84    }
85}
86
87pub struct QueueConfig {
88    pub provider: MqProvider,
89    pub brokers: Vec<String>,
90    pub group_id: Option<String>,
91    pub username: Option<String>,
92    pub password: Option<String>,
93}
94
95impl Default for QueueConfig {
96    fn default() -> Self {
97        Self {
98            provider: MqProvider::Kafka(KafkaConfig::default()),
99            brokers: vec!["localhost:9092".to_string()],
100            group_id: None,
101            username: None,
102            password: None,
103        }
104    }
105}
106
107impl QueueConfig {
108    pub fn new() -> Self {
109        Self::default()
110    }
111
112    pub fn with_provider(mut self, provider: MqProvider) -> Self {
113        self.provider = provider;
114        self
115    }
116
117    pub fn with_brokers(mut self, brokers: Vec<String>) -> Self {
118        self.brokers = brokers;
119        self
120    }
121
122    pub fn with_group(mut self, group: impl Into<String>) -> Self {
123        self.group_id = Some(group.into());
124        self
125    }
126
127    pub fn with_auth(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
128        self.username = Some(username.into());
129        self.password = Some(password.into());
130        self
131    }
132}
133
134#[derive(Debug, Clone)]
135pub enum MqProvider {
136    Kafka(KafkaConfig),
137    RabbitMQ(RabbitConfig),
138    RocketMQ(RocketConfig),
139    ActiveMQ(ActiveConfig),
140    Nats(NatsConfig),
141    Pulsar(PulsarConfig),
142}
143
144#[derive(Debug, Clone, Default)]
145pub struct KafkaConfig {
146    pub client_id: Option<String>,
147    pub acks: Option<String>,
148    pub retries: Option<u32>,
149}
150
151#[derive(Debug, Clone, Default)]
152pub struct RabbitConfig {
153    pub virtual_host: Option<String>,
154}
155
156#[derive(Debug, Clone, Default)]
157pub struct RocketConfig {
158    pub namespace: Option<String>,
159}
160
161#[derive(Debug, Clone, Default)]
162pub struct ActiveConfig {
163    pub broker_url: Option<String>,
164}
165
166#[derive(Debug, Clone, Default)]
167pub struct NatsConfig {
168    pub name: Option<String>,
169}
170
171#[derive(Debug, Clone, Default)]
172pub struct PulsarConfig {
173    pub service_url: Option<String>,
174}
175
176pub struct InMemoryQueue {
177    inner: Arc<RwLock<InMemoryQueueInner>>,
178}
179
180struct InMemoryQueueInner {
181    queues: HashMap<String, VecDeque<Message>>,
182    in_flight: HashMap<String, Message>,
183    subscribers: HashMap<String, usize>,
184    next_id: u64,
185    /// H-3 修复:每个 topic 最大消息数限制(防止 OOM)
186    /// 默认 100,000,可通过 `with_max_messages_per_topic` 调整
187    max_messages_per_topic: usize,
188}
189
190/// 默认每 topic 最大消息数(H-3 修复)
191const DEFAULT_MAX_MESSAGES_PER_TOPIC: usize = 100_000;
192
193impl InMemoryQueue {
194    pub fn new() -> Self {
195        Self::with_max_messages_per_topic(DEFAULT_MAX_MESSAGES_PER_TOPIC)
196    }
197
198    /// 创建指定每 topic 最大消息数的队列(H-3 修复)
199    ///
200    /// 当队列中消息数达到此限制时,`publish` 将返回 `MqError::Publish` 错误。
201    /// 默认 100,000,可根据内存容量调整。
202    pub fn with_max_messages_per_topic(max: usize) -> Self {
203        Self {
204            inner: Arc::new(RwLock::new(InMemoryQueueInner {
205                queues: HashMap::new(),
206                in_flight: HashMap::new(),
207                subscribers: HashMap::new(),
208                next_id: 1,
209                max_messages_per_topic: max,
210            })),
211        }
212    }
213
214    pub async fn message_count(&self, topic: &str) -> usize {
215        let inner = self.inner.read().await;
216        inner.queues.get(topic).map(|q| q.len()).unwrap_or(0)
217    }
218
219    pub async fn subscriber_count(&self, topic: &str) -> usize {
220        let inner = self.inner.read().await;
221        *inner.subscribers.get(topic).unwrap_or(&0)
222    }
223
224    pub async fn in_flight_count(&self) -> usize {
225        let inner = self.inner.read().await;
226        inner.in_flight.len()
227    }
228}
229
230impl Default for InMemoryQueue {
231    fn default() -> Self {
232        Self::new()
233    }
234}
235
236#[async_trait]
237impl MessageQueue for InMemoryQueue {
238    async fn publish(&self, topic: &str, message: &[u8]) -> Result<(), MqError> {
239        let mut inner = self.inner.write().await;
240        // H-3 修复:检查消息数限制,防止 OOM
241        let current_count = inner.queues.get(topic).map(|q| q.len()).unwrap_or(0);
242        if current_count >= inner.max_messages_per_topic {
243            return Err(MqError::Publish(format!(
244                "topic '{}' is full: {} >= {} messages (H-3 protection)",
245                topic, current_count, inner.max_messages_per_topic
246            )));
247        }
248        let id = format!("msg-{}", inner.next_id);
249        // L-2 修复:使用 checked_add 防止 u64 溢出
250        // 实际场景下 u64::MAX (1.8e19) 几乎不可能触及,但严谨起见添加检查
251        inner.next_id = inner
252            .next_id
253            .checked_add(1)
254            .ok_or_else(|| MqError::Publish("message id overflow: u64::MAX reached".to_string()))?;
255        let msg = Message {
256            id,
257            ..Message::new(topic, message.to_vec())
258        };
259        inner
260            .queues
261            .entry(topic.to_string())
262            .or_insert_with(VecDeque::new)
263            .push_back(msg);
264        Ok(())
265    }
266
267    async fn consume(&self, topic: &str) -> Result<Option<Message>, MqError> {
268        let mut inner = self.inner.write().await;
269        let queue = inner
270            .queues
271            .entry(topic.to_string())
272            .or_insert_with(VecDeque::new);
273        if let Some(msg) = queue.pop_front() {
274            inner.in_flight.insert(msg.id.clone(), msg.clone());
275            Ok(Some(msg))
276        } else {
277            Ok(None)
278        }
279    }
280
281    async fn ack(&self, message_id: &str) -> Result<(), MqError> {
282        let mut inner = self.inner.write().await;
283        inner.in_flight.remove(message_id).ok_or_else(|| {
284            MqError::NotSupported(format!("Message not found for ack: {}", message_id))
285        })?;
286        Ok(())
287    }
288
289    async fn subscribe(&self, topic: &str) -> Result<(), MqError> {
290        let mut inner = self.inner.write().await;
291        *inner.subscribers.entry(topic.to_string()).or_insert(0) += 1;
292        Ok(())
293    }
294}
295
296pub struct QueueWrapper {
297    queue: Box<dyn MessageQueue>,
298}
299
300impl QueueWrapper {
301    pub fn new(provider: MqProvider) -> Self {
302        let queue: Box<dyn MessageQueue> = match provider {
303            MqProvider::Kafka(_) => Box::new(crate::kafka::InMemoryKafkaQueue::new()),
304            MqProvider::RabbitMQ(_) => Box::new(crate::rabbitmq::InMemoryRabbitmqQueue::new()),
305            MqProvider::RocketMQ(_) => Box::new(crate::rocketmq::InMemoryRocketmqQueue::new()),
306            MqProvider::ActiveMQ(_) => Box::new(crate::activemq::InMemoryActivemqQueue::new()),
307            MqProvider::Nats(_) => Box::new(crate::nats::InMemoryNatsQueue::new()),
308            MqProvider::Pulsar(_) => Box::new(crate::pulsar::InMemoryPulsarQueue::new()),
309        };
310        Self { queue }
311    }
312
313    pub async fn publish(&self, topic: &str, message: &[u8]) -> Result<(), MqError> {
314        self.queue.publish(topic, message).await
315    }
316
317    pub async fn consume(&self, topic: &str) -> Result<Option<Message>, MqError> {
318        self.queue.consume(topic).await
319    }
320
321    pub async fn ack(&self, message_id: &str) -> Result<(), MqError> {
322        self.queue.ack(message_id).await
323    }
324
325    pub async fn subscribe(&self, topic: &str) -> Result<(), MqError> {
326        self.queue.subscribe(topic).await
327    }
328}
329
330#[cfg(test)]
331mod tests {
332    use super::*;
333
334    #[tokio::test]
335    async fn test_in_memory_queue_basic() {
336        let queue = InMemoryQueue::new();
337        queue.publish("topic1", b"hello").await.unwrap();
338        let msg = queue
339            .consume("topic1")
340            .await
341            .unwrap()
342            .expect("msg should exist");
343        assert_eq!(msg.payload, b"hello");
344        queue.ack(&msg.id).await.unwrap();
345    }
346
347    /// L-2 测试:next_id 溢出保护
348    ///
349    /// 通过将 next_id 设置为 u64::MAX,验证下一次 publish 会返回错误而非 panic
350    #[tokio::test]
351    async fn test_l2_next_id_overflow_protection() {
352        let queue = InMemoryQueue::new();
353        // 将 next_id 手动设置为 u64::MAX
354        {
355            let mut inner = queue.inner.write().await;
356            inner.next_id = u64::MAX;
357        }
358        // 此时 publish 应返回错误(L-2 修复:checked_add 失败)
359        let result = queue.publish("topic1", b"msg").await;
360        assert!(result.is_err());
361        match result {
362            Err(MqError::Publish(msg)) => {
363                assert!(
364                    msg.contains("overflow"),
365                    "expected overflow error, got: {}",
366                    msg
367                );
368            }
369            _ => panic!("Expected MqError::Publish with overflow message"),
370        }
371    }
372
373    /// L-2 测试:next_id 在 u64::MAX - 1 时仍可正常递增到 u64::MAX
374    #[tokio::test]
375    async fn test_l2_next_id_near_max() {
376        let queue = InMemoryQueue::new();
377        {
378            let mut inner = queue.inner.write().await;
379            inner.next_id = u64::MAX - 1;
380        }
381        // 第一次 publish:next_id = u64::MAX - 1 → 成功递增到 u64::MAX
382        let result1 = queue.publish("topic1", b"msg1").await;
383        assert!(result1.is_ok());
384        // 第二次 publish:next_id = u64::MAX → checked_add(1) 溢出,应返回错误
385        let result2 = queue.publish("topic1", b"msg2").await;
386        assert!(result2.is_err());
387    }
388}