由于项目是分模块开发,今天在设计支付的时候首先想到了用mq来做异步通知(支付成功的通知)
业务场景
step 1:用户在支付模块下单
step 2:用户支付
step 3:支付模块收到支付宝或微信异步通知
step 4:支付模块通知商品模块,做其他操作 比如 打印机打印小票, 修改商品订单支付状态等等
配置spring boot rabbitmq
rabbitmq:
host: 192.168.0.120
port: 5672
username: admin
password: admin
publisher-confirms: true #设置为手动
publisher-returns: true #设置为手动
listener:
simple:
acknowledge-mode: manual #设置为手动应答
virtual-host: /
首先定义队列和交换器:
/**
* @Author: lzs
* @Date: 2019/7/30 10:39
* @return:
* @throws:
*/
@Configuration
public class OrderMQConfig {
//------->商品订单
public static final String GOOD_ORDER_PAY_SUCCESS_QUEUE_NAME="goodOrderPaySuccessQueue";//商品订单充值成功队列
//public static final String GOOD_ORDER_PAY_SUCCESS_PRINT_QUEUE_NAME="goodOrderPaySuccessPrintQueue";//商品订单打印队列
public static final String GOOD_ORDER_PAY_TIMEOUT_QUEUE_NAME="goodOrderPayTimeoutQueue";//商品订单充值失败队列
//路由键
public static final String GOOD_ORDER_PAY_SUCCESS_ROUTE_KEY="goodOrderSuccess";
public static final String GOOD_ORDER_PAY_TIMEOUT_ROUTE_KEY="goodOrderTimeout";
// public static final String GOOD_ORDER_PAY_SUCCESS_PRINT_ROUTE_KEY="goodOrderSuccess";
//交换器
public static final String EXCHANGE_NAME="orderPayExchange";
//订单支付成功队列
@Bean
public Queue goodOrderPaySuccessQueue(){
return new Queue(GOOD_ORDER_PAY_SUCCESS_QUEUE_NAME,true);
}
//订单支付超时队列
@Bean
public Queue goodOrderPayTimeoutQueue(){
return new Queue(GOOD_ORDER_PAY_TIMEOUT_QUEUE_NAME,true);
}
//打印商品队列
// @Bean
// public Queue goodOrderPaySuccessPrintQueue(){
// return new Queue(GOOD_ORDER_PAY_SUCCESS_PRINT_QUEUE_NAME,true);
// }
//声明交换器
@Bean
public TopicExchange orderExchange(){
return new TopicExchange (EXCHANGE_NAME);
}
//绑定
@Bean
public Binding binding1(){
return BindingBuilder.bind(goodOrderPaySuccessQueue()).to(orderExchange()).with(GOOD_ORDER_PAY_SUCCESS_ROUTE_KEY);
}
@Bean
public Binding binding2(){
return BindingBuilder.bind(goodOrderPayTimeoutQueue()).to(orderExchange()).with(GOOD_ORDER_PAY_TIMEOUT_ROUTE_KEY);
}
//
// @Bean
// public Binding topicBindingPrint(){
// return BindingBuilder.bind(goodOrderPaySuccessPrintQueue()).to(orderExchange()).with(GOOD_ORDER_PAY_SUCCESS_ROUTE_KEY);
// }
//<-------商品订单
//------->充值订单
public static final String RECHARGE_ORDER_PAY_SUCCESS_QUEUE_NAME="rechargeOrderPaySuccessQueue";//充值订单充值成功队列
public static final String RECHARGE_ORDER_PAY_TIMEOUT_QUEUE_NAME="rechargeOrderPayTimeoutQueue";//充值订单充值失败队列
public static final String RECHARGE_ORDER_PAY_SUCCESS_ROUTE_KEY="rechargeOrderSuccess";
public static final String RECHARGE_ORDER_PAY_TIMEOUT_ROUTE_KEY="rechargeOrderTimeout";
@Bean
public Queue rechargeOrderPaySuccessQueue(){
return new Queue(RECHARGE_ORDER_PAY_SUCCESS_QUEUE_NAME,true);
}
@Bean
public Queue rechargeOrderTimeoutQueue(){
return new Queue(RECHARGE_ORDER_PAY_TIMEOUT_QUEUE_NAME,true);
}
@Bean
public Binding binding3(){
return BindingBuilder.bind(rechargeOrderPaySuccessQueue()).to(orderExchange()).with(RECHARGE_ORDER_PAY_SUCCESS_ROUTE_KEY);
}
@Bean
public Binding binding4(){
return BindingBuilder.bind(rechargeOrderTimeoutQueue()).to(orderExchange()).with(RECHARGE_ORDER_PAY_TIMEOUT_ROUTE_KEY);
}
//<-------充值订单
}
编写消息发送服务供别人调用:
此时发送消息有两种错误情况和两种成功
1:消息未到交换器,可能是网络原因或者是交换器名写错等等
如果是这种情况的话 rabbitmq会调用confirm方法,ack参数为false
2:消息到达交换器
如果是这种情况的话 rabbitmq会调用confirm方法,ack参数为true
3:消息未到达队列,可能是没有队列和你发送的交换器进行绑定,或者网络原因
如果是这种情况的话 rabbitmq会调用 returnedMessage方法
4:消息成功到达队列
不会进入returnedMessage方法
/**
* @Author: lzs
* @Date: 2019/7/30 10:31
* @return:
* @throws:
*/
@Component
public class SendMessageService implements RabbitTemplate.ReturnCallback, RabbitTemplate.ConfirmCallback {
Logger logger= LoggerFactory.getLogger(this.getClass());
@Resource
RabbitTemplate rabbitTemplate;
@Override
public void confirm(CorrelationData correlationData, boolean ack, String s) {
//消息确认
if(ack){
logger.info("SendMessage >>> 消息到达交换器:"+correlationData.getId());
}else {
logger.error("SendMessage >>> 消息未到达交换器:"+correlationData.getId());
}
}
@Override
public void returnedMessage(Message message, int i, String s, String s1, String s2) {
logger.error("SendMessage >>> 消息未到达队列:"+message.toString());
}
/**
*发送消息
* @param exchange 交换器
* @param routeKey 路由键
* @param obj 消息
*/
public void send(String exchange,String routeKey,Object obj){
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnCallback(this);
CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
this.rabbitTemplate.convertAndSend(exchange,routeKey, obj,correlationId);
logger.info("SendMessage >>> 发送消息到RabbitMQ, 交换器: "+exchange+" 路由键: "+routeKey+" 消息id: "+correlationId.getId()+" 消息内容: "+ JSONObject.toJSONString(obj));
}
}
编写消费者:
//监听订单支付成功
@RabbitListener(queues = OrderMQConfig.GOOD_ORDER_PAY_SUCCESS_QUEUE_NAME)
@Transactional
public void listenerPayOrderSuccess(String receiveMessage, Message message, Channel channel){
try {
logger.info("订单支付成功队列接收到消息: "+receiveMessage);
NetbarOrder order = JSONObject.parseObject(receiveMessage,NetbarOrder.class);
String orderid =String.valueOf(order.getOrderid());
if(!Integer.valueOf(order.getState()).equals(OrderState.UNPAY.value())){
logger.error("订单状态不符: "+JSONObject.toJSONString(order));
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
return;
}
order.setState(Integer.valueOf(OrderState.COMPLETE.value()).byteValue());
order.setPaytime(DateUtil.nowTime());
int update = netbarOrderMapper.updateByPrimaryKeySelective(order);
if(update==1){
try {
//把成功的订单id放到redis里 然后前台轮训到这个状态
redisService.sSet(RedisKey.PAY_SUCCESS_SET, orderid);
}catch (Exception e){
logger.error("订单支付成功 操作redis失败 : "+receiveMessage);
}
//打印商品订单
try {
String string =(String) redisService.hget(RedisKey.PRINT_ORDER_HMAP, orderid);
PrintOrderVo printOrderVo = JSONObject.parseObject(string, PrintOrderVo.class);
printService.printOrder(printOrderVo);
redisService.hdel(RedisKey.PRINT_ORDER_HMAP, orderid);
}catch (Exception e){
logger.error("打印商品订单出错:"+e.getMessage());
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}else {
//消息重发
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}catch (Exception e){
e.printStackTrace();
logger.error("订单支付成功异常 >>>> 订单:"+receiveMessage+" msg:"+e.getMessage());
try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
}catch (Exception e1){
logger.error("订单支付成功消息签收失败:"+e1.getMessage());
}
}
}
编写生产者:
@Resource
SendMessageService sendMessage;
@GetMapping("/send")
public void send(String msg,String key){
orderService.getOrderByOrderId(Long.valueOf(msg));
sendMessage.send(OrderMQConfig.EXCHANGE_NAME,OrderMQConfig.GOOD_ORDER_PAY_SUCCESS_ROUTE_KEY,msg);
}