场景说明:用户注册后,向用户发注册邮件和注册短信。
我们在进行以上场景开发时,通常会使用两种方式实现。
串行的方式
在没有mq中间件之前,我们通常使用这种方式实现,实现起来很容易,比如先将用户信息插入数据库,然后发送成功注册的邮件、短信。以上三个任务完成后才会给用户响应,但我们应该都知道,对于邮件、短信,对于系统核心业务来说这都不是必须马上发送的,这样的实现方式无非会增加系统的响应时间,甚至给用户带来不好的体验。可以认为就是一个线程在串行执行三个任务。
并行的方式
将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。可以认为是三个线程在同时处理不同的任务,并行执行。
有了以上的应用场景,我们可以联想到其他的应用场景,比如一个用户同时下单了多个商品,此时系统要向该用户及商家发送下单消息。这个场景主要分为两个任务:1.生成订单 2.向用户和服务商发送下单消息。而生成订单任务是系统的核心业务,发送消息的任务就可以异步执行,否则每次都要访问一次数据库插入一条消息记录,增加了下单操作的响应时间。 下面就是此应用场景的实战。
<!--RabbitMQ 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring.rabbitmq.host=59.xxx.xxx.xxx
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123
spring.rabbitmq.publisher-confirm-type=correlated
package com.cnu.stcsp.rabbitmq;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MQConfig {
// 定义交换机名称
public static final String ORDER_CREATE_MSG_EXCHANGE = "order_create_msg_exchange";
// 定义两个队列 系统发给买家的信息队列,系统发给卖家的消息队列
public static final String MSGTOBUYER_QUEUE = "msgtobuyer_queue";
public static final String MSGTOSELLER_QUEUE = "msgtoseller_queue";
// 定义两个routingKey
public static final String MSGTOBUYER_RK = "msgtobuyer_rk";
public static final String MSGTOSELLER_RK = "msgtoseller_rk";
// 声明交换机
@Bean
public DirectExchange orderMsgExchange() {
return ExchangeBuilder.directExchange(ORDER_CREATE_MSG_EXCHANGE).durable(true).build();
}
// 声明队列
@Bean
public Queue buyerMsgQueue() {
return QueueBuilder.durable(MSGTOBUYER_QUEUE).build();
}
@Bean
public Queue sellerMsgQueue() {
return QueueBuilder.durable(MSGTOSELLER_QUEUE).build();
}
// 绑定交换机和队列
@Bean
public Binding type0QueueBindingExchange(@Qualifier("buyerMsgQueue") Queue buyerMsgQueue,
@Qualifier("orderMsgExchange") DirectExchange orderMsgExchange) {
return BindingBuilder.bind(buyerMsgQueue).to(orderMsgExchange).with(MSGTOBUYER_RK);
}
@Bean
public Binding type1QueueBindingExchange(@Qualifier("sellerMsgQueue") Queue sellerMsgQueue,
@Qualifier("orderMsgExchange") DirectExchange orderMsgExchange) {
return BindingBuilder.bind(sellerMsgQueue).to(orderMsgExchange).with(MSGTOSELLER_RK);
}
}
消息队列里我存放的是Json串,首先创建一个消息对象RepealOrderVo
package com.cnu.stcsp.entity.vo.order;
public class RepealOrderVo {
private String RepealReason;
private String userId;// 买家/ 卖家 id
private String username;// 卖家 / 买家名
private String orderName;//订单名称
private String updateTime;//消息时间
public String getRepealReason() {
return RepealReason;
}
public void setRepealReason(String repealReason) {
RepealReason = repealReason;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public RepealOrderVo(String repealReason, String userId, String username, String orderName, String updateTime) {
RepealReason = repealReason;
this.userId = userId;
this.username = username;
this.orderName = orderName;
this.updateTime = updateTime;
}
public RepealOrderVo(String userId, String username, String orderName, String updateTime) {
this.userId = userId;
this.username = username;
this.orderName = orderName;
this.updateTime = updateTime;
}
public RepealOrderVo() {
}
}
创建订单接口:(生成订单时向mq发送消息)
public String createOrder(HttpServletRequest request, Orders orders) {
// 校验当前token是否存在或有效
boolean b = JwtUtils.checkToken(request);
if(!b) throw new MyExceptionHandler(20002,"创建订单失败,请尝试重新登录");
// 获取当前用户id
String currentUserId = JwtUtils.getMemberIdByJwtToken(request);
if(userService.getById(currentUserId).getIsRealNameCertification() != 1)
throw new MyExceptionHandler(20001,"请先进行实名认证");
// 获取服务id
String serveId = orders.getServeId();
// 根据服务id获取数据库对象
Serve serve = serveService.getById(serveId);
// 设值 订单名称就是服务名称
orders.setName(serve.getName());
// 订单买家Id
orders.setBuyerId(currentUserId);
// 生成订单号
orders.setOrderId(OrderIdUtils.getParentOrderCode());
// 订单状态
orders.setStatus(1);
// 订单类型
orders.setType(0);
// 订单卖家id --- 服务商
orders.setSellerId(serve.getSellerId());
// 订单类别
orders.setCategory(Integer.valueOf(serve.getCategoryId().substring(0, 1)));
int i = baseMapper.insert(orders);
// TODO // 系统给 买家 / 卖家发消息
// 发给买家消息队列
RepealOrderVo voToBuyer = new RepealOrderVo(currentUserId, userService.getById(serve.getSellerId()).getCompany(),
serve.getName(), new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
String json = JsonUtil.getJson(voToBuyer);
// 发消息给rabbitmq
rabbitTemplate.convertAndSend(MQConfig.ORDER_CREATE_MSG_EXCHANGE, MQConfig.MSGTOBUYER_RK, json, msg -> {
return msg;
});
// 卖家消息队列 系统发消息给卖家
User buyer = userService.getById(currentUserId);
RepealOrderVo voToSeller = new RepealOrderVo(serve.getSellerId(), buyer.getRealname() + "-" + buyer.getUsername(),
serve.getName(), new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
// 发消息给rabbitmq
rabbitTemplate.convertAndSend(MQConfig.ORDER_CREATE_MSG_EXCHANGE, MQConfig.MSGTOSELLER_RK, JsonUtil.getJson(voToSeller), msg -> {
return msg;
});
if(i == 1){
return orders.getId();
} else throw new MyExceptionHandler(20001, "订单生成失败");
}
package com.cnu.stcsp.rabbitmq;
import com.cnu.stcsp.commonutils.utils.JsonUtil;
import com.cnu.stcsp.entity.vo.order.RepealOrderVo;
import com.cnu.stcsp.service.MessageService;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 消息队列消费者
*/
@Component
public class OrderMsgConsumer {
@Autowired
private MessageService messageService;
@RabbitListener(queues = MQConfig.MSGTOBUYER_QUEUE) // 买家消息队列
public void receiveMsgFromBuyerQueue(Message message) {
RepealOrderVo vo = JsonUtil.getObject(new String(message.getBody()), RepealOrderVo.class);
if(vo.getUsername() == null) {
messageService.saveRemindBuyerUploadContract(vo, 1);
} else {
messageService.saveRemindBuyerUploadContract(vo, 0);
}
}
@RabbitListener(queues = MQConfig.MSGTOSELLER_QUEUE) // 卖家消息队列
public void receiveMsgFromSellerQueue(Message message) {
RepealOrderVo vo = JsonUtil.getObject(new String(message.getBody()), RepealOrderVo.class);
messageService.saveRemindSellerUploadContract(vo);
}
}