背景:我们在做订单流程开发时,几乎都会碰到下单后订单未付款,自动取消订单,库存恢复的场景,比如淘宝下单、美团下单等等;针对这种场景我们要考虑两个特性,1并发 2实时,下面有两种设计方案可实现
一.利用中间件Redis,监听其过期时间来实现(订阅键空间通知)。
-
修改redis.conf 配置。查看 notify-keyspace-events 配置项,如果没有,添加 notify-keyspace-events Ex,如果有值,则追加 Ex,相关参数说明如下:
E:keyevent 事件,事件以 keyevent@ 为前缀进行发布
x:过期事件,当某个键过期并删除时会产生该事件
2.利用Redis中间件的psubscribe命令方法消费。
具体操作
//【将此方法加入常驻进程中,监听过期key事件】
//psubscribe 第一个参数正则匹配事件
function expireEvent()
{
$redis = new Redis();
$redis->psubscribe(array('__keyevent@0__:expired'), 'keyCallback');
}
//回调处理
function keyCallback($redis, $pattern, $chan, $msg)
{
echo "Pattern: $pattern\n";
echo "Channel: $chan\n";
echo "Payload: $msg\n";
//todo 处理逻辑....库存更改 状态记录等操作
}
//client请求,设置一个订单key
function setOrderKey()
{
$redis = new Redis();
$redis->set('order_id',5,'123456');
}
二.使用RabbitMQ的过期队列与死信队列,设置消息的存活时间,在设置的时间内未被消费,即会投递到死信队列,我们监听死信队列即可
消息会变成死信消息的场景:
- 消息被(basic.reject() or basic.nack()) and requeue = false,即消息被消费者拒绝签收,并且重新入队为false。
- 有一种场景需要注意下:消费者设置了自动ACK,当重复投递次数达到了设置的最大retry次数之后,消息也会投递到死信队列,但是内部的原理还是调用了nack/reject。
- 消息过期,过了TTL存活时间。
队列设置了x-max-length最大消息数量且当前队列中的消息已经达到了这个数量,再次投递,消息将被挤掉,被挤掉的是最靠近被消费那一端的消息。
//用目前公司改造举个例子
//1.创建一个只有生产者没有消费者的队列
$queue = 'test_queue';//只有生产者无消费者的队列
$timeOut = 60;//60秒
$message = [
'order_id'=>123456
];
$rabitMq->produceMessage($message, $queue, '', $timeOut);
//2.交换机绑定 队列test_queue 到 test_queue_ttl的关系
//3.创建一支消费者的ttl队列
$queueTtl = 'test_queue_ttl';
$rabitMq->consumeMessage($queueTtl, 'keyCallback');
//4.处理
function keyCallback($message)
{
$data = json_decode($message, true);
//todo 处理逻辑....库存更改 状态记录等操作
}
【之前未写完,今天补回来】