RabbitMq如何實現---流量削峰?(一)

後端從入門到精通 發佈 2024-04-26T01:06:17.404204+00:00

搭建環境:springBoot + maven + RabbitMQ 3.8.14 + Erlang 23.2.7。

搭建環境:springBoot + maven + rabbitmq 3.8.14 + erlang 23.2.7


注意:安裝時RabbitMq和erlang版本號必須對應,以免引起不必要的bug。


1、應用場景


  1. 應用解耦:當要調用遠程系統時候,當存在訂單系統和庫存系統時,訂單系統下單,庫存系統需要收到訂單後庫存減一,這時候如果系統宕機,會造成訂單丟失,吧訂單消息發入mq,庫存系統再去mq消費,就能解決這一問題。
  2. 異步消費:傳統的模式:用戶下單—>郵件發送—>簡訊提醒,三個步驟全部完成,才能返回用戶消費成功,因為後面兩個步驟完全沒有必須是當前時間完成,可以用戶下單成功後,直接發送給mq,返回給用戶消費成功,之後郵件發送和簡訊提醒,可以其他時間段來消費發送給用戶。
  3. 流量削峰:大型雙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到隊列中。


​編輯

​編輯上面的流程處理完之後,就可以在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("處理搶單異常");
        }
    }
}



附上三個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用戶登入。


最後,看到這裡的讀者,喜歡的話安排一波(點讚,收藏,關注),原創不易,每周定期分享編程筆記。

關鍵字: