RabbitMq如何实现---流量削峰?(一)

简介: RabbitMq如何实现---流量削峰?(一)

搭建环境:springBoot + maven + RabbitMQ 3.8.14 + Erlang 23.2.7

注意:安装时rabbitMq和erlang版本号必须对应,以免引起不必要的bug。


1、应用场景


应用解耦:当要调用远程系统时候,当存在订单系统和库存系统时,订单系统下单,库存系统需要收到订单后库存减一,这时候如果系统宕机,会造成订单丢失,吧订单消息发入mq,库存系统再去mq消费,就能解决这一问题。


异步消费:传统的模式:用户下单—>邮件发送—>短信提醒,三个步骤全部完成,才能返回用户消费成功,因为后面两个步骤完全没有必须是当前时间完成,可以用户下单成功后,直接发送给mq,返回给用户消费成功,之后邮件发送和短信提醒,可以其他时间段来消费发送给用户。


流量削峰:大型双11活动时候,0点有上亿并发,这时候数据库并不能承载那么大的数据冲击,而专门为高并发设计的mq可以承受住海量的请求,发送给mq,存储成功后,再消费。


2、流量削峰


本文主要介绍流量削峰实例,先创建两个表get_redpack和send_redpack。

CREATE TABLE send_redpack(
   id int not null AUTO_INCREMENT,
     user_id varchar(32) not null comment '发红包用户',
     money decimal(10,2) not null comment '红包金额',
     unit_money decimal(10,2) not null comment '单个红包金额',
     total int not null comment '红包个数',
     remain int not null comment '红包剩余个数',
     send_date datetime not null comment '发红包时间',
     primary key(id)
);
INSERT INTO send_redpack(user_id,money, unit_money,total,remain,send_date)
VALUES("001",10000.00,10.00,1000,1000,now());
CREATE TABLE get_redpack(
  id int not null AUTO_INCREMENT,
  user_id varchar(32) not null comment '抢红包用户',
  send_redpack_id int not null comment '发红包记录id',
  money decimal(10,2) not null comment '抢的红包金额',
  get_date datetime not null comment '抢红包时间',
  primary key(id)
);


本人用的是mac电脑brew安装的rabbitMq,启动rabbitMq用brew services strat rabbitmq,启动之后访问: http://localhost:15672/


登入的账号密码用guest,登入后可以在admin里面添加一个admin管理员,配置权限,在queues里面创建一个队列redpack,供项目发用户ID到队列中。



image.png

   

image.png

上面的流程处理完之后,就可以在springboot项目中引入rabbitMq包,配置文件,及其新建上面表的实体类。

<!--rabbitmq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
# ----- RabbitMq -------- #
spring.rabbitmq.virtual-host=/
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
/**
 * 抢红包
 *
 * @author keying
 * @date 2021/6/22
 */
@Data
public class GetRedpack {
    /**
     *
     */
    private Integer id;
    /**
     * 抢红包用户
     */
    private String userId;
    /**
     * 发红包用户
     */
    private String sendRedpackId;
    /**
     * 抢的红包金额
     */
    private BigDecimal money;
    /**
     * 抢红包时间
     */
    private Date getDate;
}
/**
 * 发红包
 * @author keying
 * @date 2021/6/22
 */
@Data
public class SendRedpack {
    /**
     *
     */
    private Integer id;
    /**
     * 发红包用户
     */
    private String userId;
    /**
     * 红包金额
     */
    private BigDecimal money;
    /**
     * 单个红包金额
     */
    private BigDecimal unitMoney;
    /**
     * 红包个数
     */
    private Integer total;
    /**
     * 红包剩余个数
     */
    private Integer remain;
    /**
     * 发红包时间
     */
    private Date sendDate;
}

下面先写provider生产者的代码,进入接口发送红包,发送100个,然后把收红包的用户id发给mq:

/**
 * 生产者
 *
 * @author keying
 * @date 2021/6/22
 */
@RestController
@Slf4j
@RequestMapping("/provider")
public class ProviderController {
    @Resource
    private RabbitMqService rabbitMqService;
    @RequestMapping("/send_redpack")
    public void sendRedpack(){
        for (int i = 0; i < 100; i++) {
            rabbitMqService.sendRedpack(i);
        }
    }
}
@Resource
    private RabbitTemplate rabbitTemplate;
    @Override
    public void sendRedpack(int i) {
        rabbitTemplate.convertAndSend("redpack", i);
    }

然后写消费者代码,用@rabbitListener监听queue,队列就是刚刚在mq管路页面创建的redpack,定义的发红包用户为001,为了方便测试,在代码里写死,给消费者的类加一个@Component的注解,交给spring容器管理,消费逻辑大致就是:


1、先查看红包剩余数,大于0则继续,否则结束。


2、吧红包剩余数-1.


3、抢红包信息存入get_redpack表,存储抢红包详情。

/**
 * 消费
 *
 * @author keying
 * @date 2021/6/22
 */
@Component
@Slf4j
public class ConsumerRabbitMq {
    @Resource
    private RabbitMqMapper rabbitMqMapper;
    private static final String sendUserId = "001";
    /**
     * 需在RabbitMQ中手动创建redpack 队列,否则报错
     * @param message
     */
    @RabbitListener(queues = "redpack")
    public void getRedpack(String message){
        log.info("接收的消费红包人员: {}", message);
        try {
            //查询红包剩余个数是否大于0
            int remain = rabbitMqMapper.getRemain(sendUserId);
            if(remain > 0) {
                //扣减红包个数
                int result = rabbitMqMapper.deleteOne(sendUserId);
                if(result > 0) {
                    //3.新增用户抢红包记录
                    GetRedpack getRedpack = new GetRedpack();
                    getRedpack.setUserId(message);
                    getRedpack.setSendRedpackId(sendUserId);
                    getRedpack.setGetDate(new Date());
                    getRedpack.setMoney(new BigDecimal("10"));
                    rabbitMqMapper.insertGetRedpack(getRedpack);
                }
            }
            //异步通知用户抢红包成功
        } catch (Exception e) {
            log.error("处理抢单异常:" + e.getMessage());
            throw new RuntimeException("处理抢单异常");
        }
    }
}

附上3个sql。。

 <select id="getRemain" parameterType="java.lang.String" resultType="java.lang.Integer">
        select remain from send_redpack where user_id = #{sendUserId}
    </select>
    <delete id="deleteOne" parameterType="java.lang.String">
        update send_redpack set remain = remain-1 where user_id = #{sendUserId}
    </delete>
    <insert id="insertGetRedpack" parameterType="com.alibaba.first.model.GetRedpack">
        insert into get_redpack (user_id,send_redpack_id,money,get_date)
        values (#{userId},#{sendRedpackId},#{money},#{getDate})
    </insert>

重点、重点、重点、注意点事项(重要的事要说三遍),踩坑总结:


1、安装时候,rabbitMq和erlang版本号对应一致。

2、springboot集成rabbitMq,guest只能登入localoal,远程ip,需要创建admin用户,用admin用户登入。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
6月前
|
消息中间件 前端开发 数据库
八、SpringBoot+RabbitMQ削峰
八、SpringBoot+RabbitMQ削峰
206 1
|
消息中间件 SQL 缓存
警惕!这 8 个场景下 RocketMQ 会发生流量控制
警惕!这 8 个场景下 RocketMQ 会发生流量控制
370 1
警惕!这 8 个场景下 RocketMQ 会发生流量控制
|
消息中间件 Java RocketMQ
|
4月前
|
消息中间件 C语言 RocketMQ
消息队列 MQ操作报错合集之出现"Connection reset by peer"的错误,该如何处理
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
21天前
|
消息中间件 JSON Java
开发者如何使用轻量消息队列MNS
【10月更文挑战第19天】开发者如何使用轻量消息队列MNS
62 6
|
16天前
|
消息中间件 存储 Kafka
MQ 消息队列核心原理,12 条最全面总结!
本文总结了消息队列的12个核心原理,涵盖消息顺序性、ACK机制、持久化及高可用性等内容。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
30天前
|
消息中间件 安全 Java
云消息队列RabbitMQ实践解决方案评测
一文带你详细了解云消息队列RabbitMQ实践的解决方案优与劣
63 7
|
20天前
|
消息中间件
解决方案 | 云消息队列RabbitMQ实践获奖名单公布!
云消息队列RabbitMQ实践获奖名单公布!
|
27天前
|
消息中间件 存储 弹性计算
云消息队列RabbitMQ实践
云消息队列RabbitMQ实践
|
1月前
|
消息中间件 存储 监控
解决方案 | 云消息队列RabbitMQ实践
在实际业务中,网站因消息堆积和高流量脉冲导致系统故障。为解决这些问题,云消息队列 RabbitMQ 版提供高性能的消息处理和海量消息堆积能力,确保系统在流量高峰时仍能稳定运行。迁移前需进行技术能力和成本效益评估,包括功能、性能、限制值及费用等方面。迁移步骤包括元数据迁移、创建用户、网络打通和数据迁移。
63 4