RabbitMQ实战——异步处理

  • Post author:
  • Post category:其他




rabbitmq一个典型的使用场景就是异步处理

场景说明:用户注册后,向用户发注册邮件和注册短信。

我们在进行以上场景开发时,通常会使用两种方式实现。

  1. 串行的方式

    在没有mq中间件之前,我们通常使用这种方式实现,实现起来很容易,比如先将用户信息插入数据库,然后发送成功注册的邮件、短信。以上三个任务完成后才会给用户响应,但我们应该都知道,对于邮件、短信,对于系统核心业务来说这都不是必须马上发送的,这样的实现方式无非会增加系统的响应时间,甚至给用户带来不好的体验。


    可以认为就是一个线程在串行执行三个任务。



    在这里插入图片描述


  2. 并行的方式


    将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。


    可以认为是三个线程在同时处理不同的任务,并行执行。



    在这里插入图片描述

    有了以上的应用场景,我们可以联想到其他的应用场景,比如一个用户同时下单了多个商品,此时系统要向该用户及商家发送下单消息。这个场景主要分为两个任务:1.生成订单 2.向用户和服务商发送下单消息。

    而生成订单任务是系统的核心业务,发送消息的任务就可以异步执行,否则每次都要访问一次数据库插入一条消息记录,增加了下单操作的响应时间。

    下面就是此应用场景的实战。



1.加入springboot整合rabbitmq依赖

<!--RabbitMQ 依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>



2.application配置文件

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



3.mq架构图及配置

在这里插入图片描述

由以上的架构图,我们就可以对mq进行配置,一下是配置代码:

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);
    }

}



4.生产者生产消息


消息队列里我存放的是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, "订单生成失败");
    }



5.消费者消费消息

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);
    }
}



6.测试

  • 启动项目后,rabbitmq管理页就可以看到自动生成的交换机及其通过routingKey绑定的队列(前提时配置不出错)

    在这里插入图片描述

    在这里插入图片描述

    当用户下单时,会同时向用户和各个商家发送下单消息。(这里就不演示了)



版权声明:本文为qq_41519304原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。