钉钉 AI 客服:消息队列设计
消息队列是解耦和高可用的关键。
一、为什么需要消息队列?
| 场景 | 问题 | 解决 |
|---|---|---|
| 高并发 | 请求堆积 | 异步处理 |
| 服务解耦 | 依赖故障 | 缓冲 |
| 削峰填谷 | 流量突增 | 平滑处理 |
二、Redis 消息队列
2.1 简单队列
// 生产者
await redis.lpush('chat:queue', JSON.stringify({ userId, message }));
// 消费者
while (true) {
const data = await redis.brpop('chat:queue', 0);
const { userId, message } = JSON.parse(data[1]);
await processChat(userId, message);
}
2.2 延迟队列
// 5 秒后处理
await redis.zadd('delay:queue', Date.now() + 5000, JSON.stringify(task));
// 消费延迟任务
const tasks = await redis.zrangebyscore('delay:queue', 0, Date.now());
三、RabbitMQ 配置
3.1 队列声明
const amqp = require('amqplib');
async function setup() {
const conn = await amqp.connect('amqp://localhost');
const ch = await conn.createChannel();
await ch.assertQueue('chat:process', { durable: true });
await ch.assertQueue('chat:result', { durable: true });
return { conn, ch };
}
3.2 消息发送
async function sendChat(ch, message) {
ch.sendToQueue('chat:process', Buffer.from(JSON.stringify(message)), {
persistent: true,
expiration: 60000 // 60 秒过期
});
}
3.3 消息消费
ch.consume('chat:process', async (msg) => {
try {
const data = JSON.parse(msg.content.toString());
await processChat(data);
ch.ack(msg); // 确认消息
} catch (e) {
ch.nack(msg, false, true); // 重新入队
}
});
四、消息可靠性
4.1 消息确认
// 生产者确认
ch.publish('exchange', 'routing', content, { mandatory: true });
ch.on('return', (msg) => console.log('消息被退回'));
// 消费者确认
ch.consume('queue', (msg) => {
process(msg);
ch.ack(msg);
});
4.2 持久化
// 队列持久化
await ch.assertQueue('queue', { durable: true });
// 消息持久化
ch.sendToQueue('queue', content, { persistent: true });
五、消息重试
5.1 重试机制
const MAX_RETRIES = 3;
async function processWithRetry(msg) {
const data = JSON.parse(msg.content.toString());
const retries = data.retries || 0;
try {
await process(data);
return true;
} catch (e) {
if (retries < MAX_RETRIES) {
data.retries = retries + 1;
await sendToRetryQueue(data);
return false;
}
await sendToDeadLetter(data);
return false;
}
}
5.2 死信队列
await ch.assertQueue('dead:letter', { durable: true });
await ch.assertQueue('main:queue', {
durable: true,
deadLetterExchange: '',
deadLetterRoutingKey: 'dead:letter'
});
六、监控告警
6.1 队列监控
setInterval(async () => {
const count = await redis.llen('chat:queue');
if (count > 100) {
await sendAlert(`队列堆积:${count} 条`);
}
}, 60000);
项目地址:GitHub - dingtalk-connector-pro 有问题欢迎 Issue 或评论区交流