RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包

一、案例说明:电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验,我们都知道,秒杀类,抢购类应用都有一个共同点,即瞬时请求巨大。本文中用一个多用户抢红包来类比秒杀抢购类的应用。普通的抢红包实现如下图:
RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包_第1张图片
当开始进行抢红包时,系统将会瞬时有大量的请求(高并发),接口业务逻辑处理导致应用占据服务器资源飙升,为了避免宕机,我们可以在将此高并发量的请求拒之于系统的上层,即将抢红包请求先压入消息队列, 而不直接涌入我们的系统后台业务处理接口。对此我们的优化方案如下图:
RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包_第2张图片
我们将使用RabbitMQ 在请求与接口之间充当限流缓压(流量削锋)的角色,对RabbitMQ需要更高要求的配置,及高并发配置,配置效果图如下,具体配置详见下文:
RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包_第3张图片
对每个listener在初始化的时候设置的并发消费者的个数为5,每个 consumer 可以预监听消费拉取的消息数量为 5 个,如果同一时间处理不完,会将其缓存在 mq 的客户端等待处理!

案例具体实现git地址:https://github.com/gitcaiqing/RabbitMQ-RedPack

创建数据库和数据表

CREATE DATABASE db_redpack CHARACTER SET UTF8;

创建用户发红包记录表

CREATE TABLE tb_redpack(
	 id int not null AUTO_INCREMENT,
     userId varchar(32) not null comment '发红包用户',
     amount decimal(10,2) not null comment '红包金额',
     unitAmount decimal(10,2) not null comment '单个红包金额',
     total int not null comment '红包个数',
     remain int not null comment '红包剩余个数',
     sendDate datetime not null comment '发红包时间',
     version int default 0 not null comment '版本控制,扩展乐观锁使用',
     primary key(id)
);

创建用户抢红包记录

CREATE TABLE tb_user_redpack(
	id int not null AUTO_INCREMENT,
	userId varchar(32) not null comment '抢红包用户',
	redpackId int not null comment '发红包记录id',
	amount decimal(10,2) not null comment '抢的红包金额',
	grabDate datetime not null comment '抢红包时间',
	version int default 0 not null comment '版本控制,扩展乐观锁使用',
	primary key(id)
);

插入测试数据,这里我为了降低处理红包金额的复杂度,故意整成固定的金额,我们将发1000个红包,每个红包100.00元。可以根据自己测试需要调整数据。

INSERT INTO tb_redpack(userId,amount,unitAmount,total,remain,sendDate)
VALUES("999999",100000.00,100.00,1000,1000,now());

二、搭建项目
1.项目结构
RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包_第4张图片

2.Maven引入需要的jar包


  4.0.0
  com.sc
  RabbitMQ-RedPack
  0.0.1-SNAPSHOT
  war

  
    UTF-8
    4.1.4.RELEASE
	2.5.0
	1.2.3
  
  
  
  		
  		
			com.rabbitmq
			amqp-client
			3.4.1
		
		
       		org.springframework.amqp
       		spring-rabbit
       		1.4.0.RELEASE
    	 
    	
   		  
          
            org.springframework  
            spring-core  
            ${spring.version}  
          
  
          
            org.springframework  
            spring-web  
            ${spring.version}  
          
          
            org.springframework  
            spring-oxm  
            ${spring.version}  
          
          
            org.springframework  
            spring-tx  
            ${spring.version}  
          
  
          
            org.springframework  
            spring-jdbc  
            ${spring.version}  
          
  
          
            org.springframework  
            spring-webmvc  
            ${spring.version}  
          
          
            org.springframework  
            spring-aop  
            ${spring.version}  
          
  
          
            org.springframework  
            spring-context-support  
            ${spring.version}  
          
  
          
            org.springframework  
            spring-test  
            ${spring.version}  
         
	
		
		
			org.mybatis
			mybatis
			3.2.8
		
	
		
		
			org.mybatis
			mybatis-spring
			1.2.2
		
		
		
	    
	        com.github.miemiedev
	        mybatis-paginator
	        1.2.17
	    
	
		
		
			mysql
			mysql-connector-java
			5.1.34
		
	
		
		
			commons-dbcp
			commons-dbcp
			1.3
		
	
		
			org.aspectj
			aspectjweaver
			1.8.4
		
	
		
		
			log4j
			log4j
			1.2.17
		
		
		
			org.slf4j
			slf4j-api
			1.7.7
		
		
			org.slf4j
			slf4j-log4j12
			1.7.7
		
	
		
		
		    javax.servlet
		    javax.servlet-api
		    3.0.1
		    provided
		
		
		
			javax.servlet
			jstl
			1.2
		
	
		
		
			org.codehaus.jackson
			jackson-mapper-asl
			1.9.13
		
	
		
			com.fasterxml.jackson.core
			jackson-annotations
			${jackson.version}
		
	
		
			com.fasterxml.jackson.core
			jackson-core
			${jackson.version}
		
	
		
			com.fasterxml.jackson.core
			jackson-databind
			${jackson.version}
		
		
		
		
		
			org.apache.commons
			commons-lang3
			3.3.2
		
	
  
  
    
		
			org.apache.maven.plugins
			maven-compiler-plugin
			3.2
			
				1.7
				1.7
			
		
	
  

3.web.xml配置



   RabbitMQ-RedPack	
   
     
    
		contextConfigLocation 
 		 classpath*:spring/*.xml,classpath*:servlet/*.xml 
 	 
 	
 	  
	
		org.springframework.web.context.ContextLoaderListener
	
	
      
	
		encodingFilter
		org.springframework.web.filter.CharacterEncodingFilter
		
			encoding
			utf-8
		
		
			forceEncoding
			true
		
	
	
		encodingFilter
		/*
	
	
	  
	
		springMvc
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			
				classpath*:servlet/*.xml
			
		
		1
	
	
		springMvc
		/
	
  
	
		30
	
	
		index.jsp
	

4.JDBC和RabbitMQ连接配置
jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/db_redpack?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=2
jdbc.maxActive=5
jdbc.maxIdle=5
jdbc.minIdle=1
jdbc.validationQuery=select 1

config.properties

#RabbitMQ 连接配置
rmq.host=127.0.0.1
rmq.port=5672
rmq.user=guest
rmq.password=guest
rmq.channelCacheSize=25
rmq.virtual=/

5.Spring,Mybatis,数据源配置
spring/applicationContext.xml




	
	
	
		
	


spring/dataAccessContext.xml



	数据库、事务配置
	
	
		
		
		
		
		
		
		
		
		
	
	
	
	
	
		
	

spring/mybatis.xml



	
	
		
		
			 
                classpath:sql/*.xml
            
		
	

	
		
		
	
	
	
  		
	

6.SpringMVC配置 servlet/spring-mvc.xml



		
	
    
    
    
       
       
    	
    
    
    
		
			
			
		    	
			
			  
			
			
                
            			
  		
	
	
	
	
	
    
    
    
    	
    	
    	
    
    
    

7.Spring RabbitMQ配置 spring/spring-rabbitmq.xml

 
	Spring RabbitMQ 路由模式(Routing)配置
	
	
	
	
 	
 	
 	
 	
 	
 
 	
 	
 		
 			
           		10000
        	
 		
 	
 	
 	
 	
 		
 			
 		
 	
 	
 	
 	

	
 	
	
	
	
	
 	
 		
 	
 	

8.根据表结构生成对应的实体类和SQL映射文件
参考文章 https://mp.csdn.net/mdeditor/84586914 自动生成文件。也可在我的git中下载本项目源码。地址在文章开头已给出,下面我们贴出具体实现,参考实现时给你们可以偷个懒,哈哈。
src/main/java/com/sc/entity/Redpack.java

package com.sc.entity;

import java.math.BigDecimal;
import java.util.Date;

public class Redpack {
    /**
     * 
     */
    private Integer id;

    /**
     * 发红包用户
     */
    private String userid;

    /**
     * 红包金额
     */
    private BigDecimal amount;

    /**
     * 单个红包金额
     */
    private BigDecimal unitamount;

    /**
     * 红包个数
     */
    private Integer total;

    /**
     * 红包剩余个数
     */
    private Integer remain;

    /**
     * 发红包时间
     */
    private Date senddate;

    /**
     * 版本控制,扩展乐观锁使用
     */
    private Integer version;

    /**
     * 
     * @return id 
     */
    public Integer getId() {
        return id;
    }

    /**
     * 
     * @param id 
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 发红包用户
     * @return userId 发红包用户
     */
    public String getUserid() {
        return userid;
    }

    /**
     * 发红包用户
     * @param userid 发红包用户
     */
    public void setUserid(String userid) {
        this.userid = userid == null ? null : userid.trim();
    }

    /**
     * 红包金额
     * @return amount 红包金额
     */
    public BigDecimal getAmount() {
        return amount;
    }

    /**
     * 红包金额
     * @param amount 红包金额
     */
    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    /**
     * 单个红包金额
     * @return unitAmount 单个红包金额
     */
    public BigDecimal getUnitamount() {
        return unitamount;
    }

    /**
     * 单个红包金额
     * @param unitamount 单个红包金额
     */
    public void setUnitamount(BigDecimal unitamount) {
        this.unitamount = unitamount;
    }

    /**
     * 红包个数
     * @return total 红包个数
     */
    public Integer getTotal() {
        return total;
    }

    /**
     * 红包个数
     * @param total 红包个数
     */
    public void setTotal(Integer total) {
        this.total = total;
    }

    /**
     * 红包剩余个数
     * @return remain 红包剩余个数
     */
    public Integer getRemain() {
        return remain;
    }

    /**
     * 红包剩余个数
     * @param remain 红包剩余个数
     */
    public void setRemain(Integer remain) {
        this.remain = remain;
    }

    /**
     * 发红包时间
     * @return sendDate 发红包时间
     */
    public Date getSenddate() {
        return senddate;
    }

    /**
     * 发红包时间
     * @param senddate 发红包时间
     */
    public void setSenddate(Date senddate) {
        this.senddate = senddate;
    }

    /**
     * 版本控制,扩展乐观锁使用
     * @return version 版本控制,扩展乐观锁使用
     */
    public Integer getVersion() {
        return version;
    }

    /**
     * 版本控制,扩展乐观锁使用
     * @param version 版本控制,扩展乐观锁使用
     */
    public void setVersion(Integer version) {
        this.version = version;
    }
}

src/main/java/com/sc/entity/UserRedpack.java

package com.sc.entity;

import java.math.BigDecimal;
import java.util.Date;

public class UserRedpack {
    /**
     * 
     */
    private Integer id;

    /**
     * 抢红包用户
     */
    private String userid;

    /**
     * 发红包记录id
     */
    private Integer redpackid;

    /**
     * 抢的红包金额
     */
    private BigDecimal amount;

    /**
     * 抢红包时间
     */
    private Date grabdate;

    /**
     * 版本控制,扩展乐观锁使用
     */
    private Integer version;

    /**
     * 
     * @return id 
     */
    public Integer getId() {
        return id;
    }

    /**
     * 
     * @param id 
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 抢红包用户
     * @return userId 抢红包用户
     */
    public String getUserid() {
        return userid;
    }

    /**
     * 抢红包用户
     * @param userid 抢红包用户
     */
    public void setUserid(String userid) {
        this.userid = userid == null ? null : userid.trim();
    }

    /**
     * 发红包记录id
     * @return redpackId 发红包记录id
     */
    public Integer getRedpackid() {
        return redpackid;
    }

    /**
     * 发红包记录id
     * @param redpackid 发红包记录id
     */
    public void setRedpackid(Integer redpackid) {
        this.redpackid = redpackid;
    }

    /**
     * 抢的红包金额
     * @return amount 抢的红包金额
     */
    public BigDecimal getAmount() {
        return amount;
    }

    /**
     * 抢的红包金额
     * @param amount 抢的红包金额
     */
    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    /**
     * 抢红包时间
     * @return grabDate 抢红包时间
     */
    public Date getGrabdate() {
        return grabdate;
    }

    /**
     * 抢红包时间
     * @param grabdate 抢红包时间
     */
    public void setGrabdate(Date grabdate) {
        this.grabdate = grabdate;
    }

    /**
     * 版本控制,扩展乐观锁使用
     * @return version 版本控制,扩展乐观锁使用
     */
    public Integer getVersion() {
        return version;
    }

    /**
     * 版本控制,扩展乐观锁使用
     * @param version 版本控制,扩展乐观锁使用
     */
    public void setVersion(Integer version) {
        this.version = version;
    }
}

src/main/java/com/sc/mapper/ResultModel.java

package com.sc.entity;

public class ResultModel {

	public int resultCode = 0;//0成功1失败
	public T data;
	public String msg;
	public int getResultCode() {
		return resultCode;
	}
	public ResultModel(int resultCode, T data, String msg) {
		super();
		this.resultCode = resultCode;
		this.data = data;
		this.msg = msg;
	}
	public void setResultCode(int resultCode) {
		this.resultCode = resultCode;
	}
	public T getData() {
		return data;
	}
	public void setData(T data) {
		this.data = data;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	
}

src/main/java/com/sc/mapper/RedpackMapper.java

import com.sc.entity.Redpack;

public interface RedpackMapper {
	
    int deleteByPrimaryKey(Integer id);
    
    int insert(Redpack record);
    
    int insertSelective(Redpack record);
    
    Redpack selectByPrimaryKey(Integer id);
    
    int updateByPrimaryKeySelective(Redpack record);
    
    int updateByPrimaryKey(Redpack record);
    
    /**
     * 查询红包剩余个数
     * @param id
     * @return
     */
    int selectRemainByPrimaryKey(Integer id);

    /**
     * 扣减红包剩余个数
     * @param id
     * @return
     */
	int updateRemainRedPack(Integer id);
}

src/main/java/com/sc/mapper/UserRedpackMapper.java

package com.sc.mapper;

import com.sc.entity.UserRedpack;

public interface UserRedpackMapper {
    int deleteByPrimaryKey(Integer id);
    
    int insert(UserRedpack record);
    
    int insertSelective(UserRedpack record);
    
    UserRedpack selectByPrimaryKey(Integer id);
    
    int updateByPrimaryKeySelective(UserRedpack record);
    
    int updateByPrimaryKey(UserRedpack record);
}

src/main/resources/sql/UserRedpackMapper.xml




  
    
    
    
    
    
    
  
  
    id, userId, redpackId, amount, grabDate, version
  
  
  
    delete from tb_user_redpack
    where id = #{id,jdbcType=INTEGER}
  
  
    insert into tb_user_redpack (id, userId, redpackId, 
      amount, grabDate, version
      )
    values (#{id,jdbcType=INTEGER}, #{userid,jdbcType=VARCHAR}, #{redpackid,jdbcType=INTEGER}, 
      #{amount,jdbcType=DECIMAL}, #{grabdate,jdbcType=TIMESTAMP}, #{version,jdbcType=INTEGER}
      )
  
  
    insert into tb_user_redpack
    
      
        id,
      
      
        userId,
      
      
        redpackId,
      
      
        amount,
      
      
        grabDate,
      
      
        version,
      
    
    
      
        #{id,jdbcType=INTEGER},
      
      
        #{userid,jdbcType=VARCHAR},
      
      
        #{redpackid,jdbcType=INTEGER},
      
      
        #{amount,jdbcType=DECIMAL},
      
      
        #{grabdate,jdbcType=TIMESTAMP},
      
      
        #{version,jdbcType=INTEGER},
      
    
  
  
    update tb_user_redpack
    
      
        userId = #{userid,jdbcType=VARCHAR},
      
      
        redpackId = #{redpackid,jdbcType=INTEGER},
      
      
        amount = #{amount,jdbcType=DECIMAL},
      
      
        grabDate = #{grabdate,jdbcType=TIMESTAMP},
      
      
        version = #{version,jdbcType=INTEGER},
      
    
    where id = #{id,jdbcType=INTEGER}
  
  
    update tb_user_redpack
    set userId = #{userid,jdbcType=VARCHAR},
      redpackId = #{redpackid,jdbcType=INTEGER},
      amount = #{amount,jdbcType=DECIMAL},
      grabDate = #{grabdate,jdbcType=TIMESTAMP},
      version = #{version,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  

src/main/resources/sql/RedpackMapper.xml




  
    
    
    
    
    
    
    
    
  
  
    id, userId, amount, unitAmount, total, remain, sendDate, version
  
  
  
    delete from tb_redpack
    where id = #{id,jdbcType=INTEGER}
  
  
    insert into tb_redpack (id, userId, amount, 
      unitAmount, total, remain, 
      sendDate, version)
    values (#{id,jdbcType=INTEGER}, #{userid,jdbcType=VARCHAR}, #{amount,jdbcType=DECIMAL}, 
      #{unitamount,jdbcType=DECIMAL}, #{total,jdbcType=INTEGER}, #{remain,jdbcType=INTEGER}, 
      #{senddate,jdbcType=TIMESTAMP}, #{version,jdbcType=INTEGER})
  
  
    insert into tb_redpack
    
      
        id,
      
      
        userId,
      
      
        amount,
      
      
        unitAmount,
      
      
        total,
      
      
        remain,
      
      
        sendDate,
      
      
        version,
      
    
    
      
        #{id,jdbcType=INTEGER},
      
      
        #{userid,jdbcType=VARCHAR},
      
      
        #{amount,jdbcType=DECIMAL},
      
      
        #{unitamount,jdbcType=DECIMAL},
      
      
        #{total,jdbcType=INTEGER},
      
      
        #{remain,jdbcType=INTEGER},
      
      
        #{senddate,jdbcType=TIMESTAMP},
      
      
        #{version,jdbcType=INTEGER},
      
    
  
  
    update tb_redpack
    
      
        userId = #{userid,jdbcType=VARCHAR},
      
      
        amount = #{amount,jdbcType=DECIMAL},
      
      
        unitAmount = #{unitamount,jdbcType=DECIMAL},
      
      
        total = #{total,jdbcType=INTEGER},
      
      
        remain = #{remain,jdbcType=INTEGER},
      
      
        sendDate = #{senddate,jdbcType=TIMESTAMP},
      
      
        version = #{version,jdbcType=INTEGER},
      
    
    where id = #{id,jdbcType=INTEGER}
  
  
    update tb_redpack
    set userId = #{userid,jdbcType=VARCHAR},
      amount = #{amount,jdbcType=DECIMAL},
      unitAmount = #{unitamount,jdbcType=DECIMAL},
      total = #{total,jdbcType=INTEGER},
      remain = #{remain,jdbcType=INTEGER},
      sendDate = #{senddate,jdbcType=TIMESTAMP},
      version = #{version,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  
  
  
  
  
  	update tb_redpack set remain = remain - 1
  	where id = #{id,jdbcType=INTEGER}
  

9.定义发送队列消息、红包、用户红包、抢红包业务Service接口
src/main/java/com/sc/service/CommonMqService.java

package com.sc.service;

public interface CommonMqService {

	//插入用户抢红包信息,信息内容为用户id
	void sendGradRedPack(String userid);
}

src/main/java/com/sc/service/RedpackService.java

package com.sc.service;

public interface RedpackService {
	
	//查询红包个数
	int getRedPackRemain(Integer id);
	
	//扣减红包个数
	int deducteRedPack(Integer id);
	
}

src/main/java/com/sc/service/UserRedpackService.java

package com.sc.service;

import com.sc.entity.UserRedpack;

public interface UserRedpackService {

	//新增用户抢红包记录
	int insertGradReadPack(UserRedpack userRedpack);
}

src/main/java/com/sc/service/GrabRedPackService.java

package com.sc.service;

public interface GrabRedPackService {
	//抢红包业务
	void grabRedPack(String userId);
}

10.定义发送队列消息、红包、用户红包、抢红包业务Service接口具体实现
src/main/java/com/sc/service/impl/CommonMqServiceImpl.java

package com.sc.service.impl;    
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sc.service.CommonMqService;

@Service
public class CommonMqServiceImpl implements CommonMqService{
	
	private static final Logger log = LoggerFactory.getLogger(CommonMqServiceImpl.class);

	@Autowired
	private RabbitTemplate rabbitTemplate;
	
	/**
	 * 发送消息
	 */
	public void sendGradRedPack(String userid) {
		try {
			rabbitTemplate.convertAndSend("info", userid);
		} catch (AmqpException e) {
			log.error("发送用户抢红包进入消息队列异常:"+e.getMessage());
			e.printStackTrace();
		}
	}

}

src/main/java/com/sc/service/impl/RedpackServiceImpl.java

package com.sc.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sc.mapper.RedpackMapper;
import com.sc.service.RedpackService;

@Service
public class RedpackServiceImpl implements RedpackService{

	@Autowired
	private RedpackMapper redpackMapper;

	/**
	 * 获取红包剩余个数
	 * @param id
	 * @return
	 */
	public int getRedPackRemain(Integer id) {
		return redpackMapper.selectRemainByPrimaryKey(id);
	}

	/**
	 * 扣减红包剩余个数
	 */
	public int deducteRedPack(Integer id) {
		return redpackMapper.updateRemainRedPack(id);
	}

}

src/main/java/com/sc/service/impl/UserRedpackServiceImpl.java

package com.sc.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sc.entity.UserRedpack;
import com.sc.mapper.UserRedpackMapper;
import com.sc.service.UserRedpackService;

@Service
public class UserRedpackServiceImpl implements UserRedpackService{
	
	@Autowired
	private UserRedpackMapper userRedpackMapper;

	/**
	 * 插入用户抢红包记录
	 */
	public int insertGradReadPack(UserRedpack userRedpack) {
		return userRedpackMapper.insertSelective(userRedpack);
	}

}

src/main/java/com/sc/service/impl/GrabRedPackServiceImpl.java
主要的业务实现包括红包个数校验,更新红包剩余个数,插入用户抢红包记录

package com.sc.service.impl;

import java.math.BigDecimal;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sc.entity.UserRedpack;
import com.sc.service.GrabRedPackService;
import com.sc.service.RedpackService;
import com.sc.service.UserRedpackService;

@Service
public class GrabRedPackServiceImpl implements GrabRedPackService{
	
	private static final Logger log = LoggerFactory.getLogger(GrabRedPackServiceImpl.class);
	
	private static final int redpackId = 1;
	private static final String amount = "10.00";
	
	@Autowired
	private RedpackService redpackService;
	@Autowired
	private UserRedpackService userRedpackService;

	public void grabRedPack(String userId) {
		try {
			//1.查询红包剩余个数是否大于0
			int remain = redpackService.getRedPackRemain(redpackId);
			if(remain > 0) {
				//2.扣减红包个数
				int result = redpackService.deducteRedPack(redpackId);
				if(result > 0) {
					//3.新增用户抢红包记录
					UserRedpack userRedpack = new UserRedpack();
					userRedpack.setUserid(userId);
					userRedpack.setRedpackid(redpackId);
					userRedpack.setGrabdate(new Date());
					userRedpack.setAmount(new BigDecimal(amount));
					userRedpackService.insertGradReadPack(userRedpack);
				}
				
			}
			//异步通知用户抢红包成功
		} catch (Exception e) {
			log.error("处理抢单异常:"+e.getMessage());
			throw new RuntimeException("处理抢单异常");
		}
	}
	
}

11.监听抢红包信息,调用抢红包业务方法
src/main/java/com/sc/consumer/RedPackConsumer.java

package com.sc.consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;

import com.rabbitmq.client.Channel;
import com.sc.service.GrabRedPackService;

public class RedPackConsumer implements ChannelAwareMessageListener{
	
	private static final Logger log = LoggerFactory.getLogger(RedPackConsumer.class);
	
	@Autowired
	private GrabRedPackService grabRedPackService;

	/**
	 * 监听用户抢红包信息
	 */
	public void onMessage(Message message, Channel channel) throws Exception {
		long tag = message.getMessageProperties().getDeliveryTag();
		try {
			String userId = new String(message.getBody(),"UTF-8");
			log.info("监听到抢红包用户:"+userId);
			//执行抢红包业务
			grabRedPackService.grabRedPack(userId);
			//手动确认
			channel.basicAck(tag, false);
		} catch (Exception e) {
			e.printStackTrace();
			log.error("用户抢红包发生异常:"+e.getMessage());
			//拒绝接收
			channel.basicReject(tag, false);
		}
	}

}

12.抢红包请求控制器

src/main/java/com/sc/controller/GrabRedPackController.java
package com.sc.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.sc.entity.ResultModel;
import com.sc.service.CommonMqService;
import com.sc.service.impl.CommonMqServiceImpl;

@Controller
@RequestMapping("redpack")
public class GrabRedPackController {
	
	private static final Logger log = LoggerFactory.getLogger(CommonMqServiceImpl.class);
	
	@Autowired
	private CommonMqService commonMqService;
	
	@RequestMapping("grab")
	@ResponseBody
	public ResultModel grab(String userid){
		try {
			commonMqService.sendGradRedPack(userid);
			return new ResultModel(0, userid, "抢红包成功");
		} catch (Exception e) {
			e.printStackTrace();
			log.error("用户:"+userid+" 抢红包失败");
			return new ResultModel(0, userid, "抢红包失败");
		}
	}

}

13.浏览器JS模拟消费者请求抢红包
这里模拟了1000个消费者抢红包。为了更好的测试可以提高并发量

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>



<%
	String path = request.getContextPath();
%>

Insert title here



	



14.执行结果查看是否插入了1000条用户抢红包记录,结果如下,证明执行OK
在高并发量的情况下可能会出现超发的情况,这就需要后台加读写锁或者结合redis处理。本文就不做详细讲解,有时间后续在进一个深入。
RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包_第5张图片

你可能感兴趣的:(消息中间件)