搭建環境: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到隊列中。
編輯
編輯上面的流程處理完之後,就可以在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用戶登入。
最後,看到這裡的讀者,喜歡的話安排一波(點讚,收藏,關注),原創不易,每周定期分享編程筆記。