应用场景:用户下单未支付,需要系统自动取消订单
解决方案:
1,实现定时器轮询订单,将未支付满30分钟后的订单,进行更新状态,变为取消订单
2,用户查看订单时,先将之前未支付的订单进行更新为取消订单
3,延时队列实现取消订单
ps:还有其它方案,一时想不起来了
优缺点:
方案一:
优点:实现简单
缺点:轮询太消耗资源了,数据库说- -噢顶木住啦
方案二:
优点:实现简了,如果不需要下单减库存或者锁库存操作,该方案实用。
缺点:下单时已经锁定了库存,简单说,如果不取消订单,库存就一直被锁。老板说》你走人吧
方案三:
优点:好用!!!!
代码实现:
连接类:
public class RabbitmqConfiguration {
private final String SERVER_HOST="127.0.0.1";//rabbitmq 服务器地址
private final int PORT=5672;//端口号
private final String USER_NAME="guest";//用户名
private final String PASSWORD="guest";//密码
private final boolean QUEUE_SAVE =true;//队列是否持久化
private final String MESSAGE_SAVE = "1" ;//消息持久化 1,0
//rabbitmq 连接工厂
private final ConnectionFactory RAB_FACTORY = new ConnectionFactory();
private Connection connection;
public void init() throws Exception{
RAB_FACTORY.setHost(SERVER_HOST);
RAB_FACTORY.setPort(PORT);
RAB_FACTORY.setUsername(USER_NAME);
RAB_FACTORY.setPassword(PASSWORD);
RAB_FACTORY.setVirtualHost("/");
this.connection = RAB_FACTORY.newConnection();
}
public Connection getConnection() {
return connection;
}
public void setConnection(Connection connection) {
this.connection = connection;
}
public boolean isQUEUE_SAVE() {
return QUEUE_SAVE;
}
public String getMESSAGE_SAVE() {
return MESSAGE_SAVE;
}
}
生产者:
/**
*
* 提供延时队列发送方法
*/
public class MQOrderOverTimeQueue {
private static final Logger log= LoggerFactory.getLogger(MQOrderOverTimeQueue.class);
private RabbitmqConfiguration rabConf;
//队列名称
//****==================订单延时队列=======================*****//
//订单延时队列
public final String DELAY_QUEUE_NAME = "delay-queue-orderOverTime";
//订单延时队列死信交换的交换器名称
public final String EXCHANGENAME = "exchange-orderOverTime";
//订单延时队列死信的交换器路由key
public final String ROUTINGKEY = "routingKey-orderOverTime";
private Channel delayChannel;//延时队列连接通道
private Channel consumerChannel;//消费队列连接通道
public void init() throws Exception{
//创建连接通道
delayChannel=rabConf.getConnection().createChannel();
delayChannel.confirmSelect();//开启confirm事务
/**创建处理延时消息的延时队列*/
Map arg = new HashMap ();
//配置死信交换器
arg.put("x-dead-letter-exchange",EXCHANGENAME); //交换器名称
//死信交换路由key (交换器可以将死信交换到很多个其他的消费队列,可以用不同的路由key 来将死信路由到不同的消费队列去)
arg.put("x-dead-letter-routing-key", ROUTINGKEY);
delayChannel.queueDeclare(DELAY_QUEUE_NAME, rabConf.isQUEUE_SAVE(), false, false, arg);
}
/**
* 方法描述: 发送延迟订单处理消息
* @param msg 消息内容 (订单号或者json格式字符串)
* @param overTime 消息存活时间
* @throws Exception
*/
public void sendMessage(String msg,Long overTime) throws Exception{
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.expiration(overTime.toString()) //设置消息存活时间(毫秒)
.build();
delayChannel.basicPublish("",DELAY_QUEUE_NAME, properties, msg.getBytes("UTF-8"));
if (delayChannel.waitForConfirms()){
log.info("send success");
}else{
log.info("send false");
}
}
public RabbitmqConfiguration getRabConf() {
return rabConf;
}
public void setRabConf(RabbitmqConfiguration rabConf) {
this.rabConf = rabConf;
}
public MQOrderOverTimeQueue() {
RabbitmqConfiguration rf= new RabbitmqConfiguration();
try {
rf.init();
this.rabConf = rf;
this.init();
} catch (Exception e) {
e.printStackTrace();
}
}
}
消费者:
**
* 延时队列接收,项目启动时,会默认启动
*/
@Component
public class MQOrderOverTimeRecv {
private static final Logger log=LoggerFactory.getLogger(MQOrderOverTimeRecv.class);
private RabbitmqConfiguration rabConf;
@Resource
private ShopOrderMapper shopOrderMapper;
@Resource
private ShopGoodsSpecidMapper shopGoodsSpecidMapper;
//队列名称
//****==================订单延时队列=======================*****//
//订单延时队列
public final String DELAY_QUEUE_NAME = "delay-queue-orderOverTime";
//订单延时消费队列
public final String CONSUME_QUEUE_NAME = "consume-queue-orderOverTime";
//订单延时队列死信交换的交换器名称
public final String EXCHANGENAME = "exchange-orderOverTime";
//订单延时队列死信的交换器路由key
public final String ROUTINGKEY = "routingKey-orderOverTime";
private Channel consumerChannel;//消费队列连接通道
public void init() throws Exception{
//创建连接通道
// delayChannel=rabConf.getConnection().createChannel();
consumerChannel=rabConf.getConnection().createChannel();
//创建交换器
consumerChannel.exchangeDeclare(EXCHANGENAME,"direct");
/**创建消费队列*/
consumerChannel.queueDeclare(CONSUME_QUEUE_NAME, rabConf.isQUEUE_SAVE(), false, false, null);
//参数1:绑定的队列名 参数2:绑定至哪个交换器 参数3:绑定路由key
consumerChannel.queueBind(CONSUME_QUEUE_NAME, EXCHANGENAME,ROUTINGKEY);
//最多接受条数 0为无限制,每次消费消息数(根据实际场景设置),true=作用于整channel,false=作用于具体的消费者
consumerChannel.basicQos(0,10, false);
//创建消费队列的消费者
Consumer consumer = new DefaultConsumer(consumerChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
try {
//业务逻辑处理
ConsumeMessage(message);
//确认消息已经消费 参数2(true=设置后续消息为自动确认消费 false=为手动确认)
consumerChannel.basicAck(envelope.getDeliveryTag(), false);
}catch (Exception e) {
e.printStackTrace();
}
}
};
boolean flag=false;//是否手动确认消息 true 是 false否
consumerChannel.basicConsume(CONSUME_QUEUE_NAME, flag, consumer);
}
/**
*
* 方法描述:监听队列consume-queue-orderOverTime中的消息,用于更新商品订单
* 业务逻辑说明: TODO(总结性的归纳方法业务逻辑)
* @param msg 消费消息(订单号,或特定格式json字符串)
* @throws InterruptedException
*/
public void ConsumeMessage(String msg) throws InterruptedException {
sout("处理业务”);
}
}
public RabbitmqConfiguration getRabConf() {
return rabConf;
}
public void setRabConf(RabbitmqConfiguration rabConf) {
this.rabConf = rabConf;
}
}
------------------------------------------------------------上文出自胖胖,转载请附带原文链接
后续更新自学的方法,以及java知识总结.
如果想交流自学可以加我微信(15979939343).
我是哪怕前路坎坷,也不愿负年轻的菜狗,自学之路,共勉。