搭建环境: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("处理抢单异常"); } } }
附上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用户登入。