1. 程式人生 > >RabitMQ系列之 Spring RabbitMQ流量削鋒實現案例之搶紅包

RabitMQ系列之 Spring RabbitMQ流量削鋒實現案例之搶紅包

一、案例說明:電商的秒殺和搶購,對我們來說,都不是一個陌生的東西。然而,從技術的角度來說,這對於Web系統是一個巨大的考驗,我們都知道,秒殺類,搶購類應用都有一個共同點,即瞬時請求巨大。本文中用一個多使用者搶紅包來類比秒殺搶購類的應用。普通的搶紅包實現如下圖:
在這裡插入圖片描述
當開始進行搶紅包時,系統將會瞬時有大量的請求(高併發),介面業務邏輯處理導致應用佔據伺服器資源飆升,為了避免宕機,我們可以在將此高併發量的請求拒之於系統的上層,即將搶紅包請求先壓入訊息佇列, 而不直接湧入我們的系統後臺業務處理介面。對此我們的優化方案如下圖:
在這裡插入圖片描述
我們將使用RabbitMQ 在請求與介面之間充當限流緩壓(流量削鋒)的角色,對RabbitMQ需要更高要求的配置,及高併發配置,配置效果圖如下,具體配置詳見下文:
在這裡插入圖片描述


對每個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 '紅包個數',
	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個紅包,每個紅包10.00元。

INSERT INTO tb_redpack(userId,amount,unitAmount,total,sendDate)
VALUES("9999",100000.00,10.00,10000,now());

二、搭建專案
1.專案結構
在這裡插入圖片描述

2.Maven引入需要的jar包

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.sc</groupId>
  <artifactId>RabbitMQ-RedPack</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.1.4.RELEASE</spring.version>
	<jackson.version>2.5.0</jackson.version>
	<shiro.version>1.2.3</shiro.version>
  </properties>
  
  <dependencies>
  		<!--rabbitmq包 -->
  		<dependency>
			<groupId>com.rabbitmq</groupId>
			<artifactId>amqp-client</artifactId>
			<version>3.4.1</version>
		</dependency>
		<dependency>
       		<groupId>org.springframework.amqp</groupId>
       		<artifactId>spring-rabbit</artifactId>
       		<version>1.4.0.RELEASE</version>
    	</dependency> 
    	
   		<!-- spring核心包 -->  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-core</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-web</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-oxm</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-tx</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-jdbc</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-webmvc</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-aop</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-context-support</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  
  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-test</artifactId>  
            <version>${spring.version}</version>  
        </dependency> 
	
		<!-- mybatis 包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.2.8</version>
		</dependency>
	
		<!--mybatis spring 外掛 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.2.2</version>
		</dependency>
		
		<!-- mybatis分頁外掛 -->
	    <dependency>
	        <groupId>com.github.miemiedev</groupId>
	        <artifactId>mybatis-paginator</artifactId>
	        <version>1.2.17</version>
	    </dependency>
	
		<!-- mysql連線 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.34</version>
		</dependency>
	
		<!-- 資料來源 -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.3</version>
		</dependency>
	
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.4</version>
		</dependency>
	
		<!-- log4j -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.7</version>
		</dependency>
	
		<!-- servlet -->
		<dependency>
		    <groupId>javax.servlet</groupId>
		    <artifactId>javax.servlet-api</artifactId>
		    <version>3.0.1</version>
		    <scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	
		<!-- json -->
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.13</version>
		</dependency>
	
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>${jackson.version}</version>
		</dependency>
	
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>${jackson.version}</version>
		</dependency>
	
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		
		
		<!-- 常用工具包 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.3.2</version>
		</dependency>
	
  </dependencies>
  <build>
    <plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.2</version>
			<configuration>
				<source>1.7</source>
				<target>1.7</target>
			</configuration>
		</plugin>
	</plugins>
  </build>
</project>

3.web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
   <display-name>RabbitMQ-RedPack</display-name>	
   
   <!-- 配置檔案 -->  
   <context-param> 
		<param-name>contextConfigLocation</param-name> 
 		<param-value> classpath*:spring/*.xml,classpath*:servlet/*.xml</param-value> 
 	</context-param> 
 	
 	<!-- Spring監聽器 -->  
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
    <!-- 編碼過濾器 -->  
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- Spring MVC servlet -->  
	<servlet>
		<servlet-name>springMvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
				classpath*:servlet/*.xml
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>springMvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
  
	<session-config>
		<session-timeout>30</session-timeout>
	</session-config>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

4.JDBC和RabbitMQ連線配置
jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/db_redpack?useUnicode=true&amp;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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"  
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:task="http://www.springframework.org/schema/task" 
	   xmlns:cache="http://www.springframework.org/schema/cache" 
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 
		http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd 
		http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

	<!--引入配置屬性檔案 -->
	<context:property-placeholder location="classpath*:config/*.properties" />
	<context:component-scan base-package="com.sc">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
	</context:component-scan>

</beans>

spring/dataAccessContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"  xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
	<description>資料庫、事務配置</description>
	<!-- 資料來源定義,使用Apache DBCP 連線池 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="initialSize" value="${jdbc.initialSize}" />
		<property name="maxActive" value="${jdbc.maxActive}" />
		<property name="maxIdle" value="${jdbc.maxIdle}" />
		<property name="minIdle" value="${jdbc.minIdle}" />
		<property name="validationQuery" value="${jdbc.validationQuery}" />
	</bean>
	<!-- 使用annotation定義事務,在每個需要事務的類上,請加上@Transactional註解 -->
	<tx:annotation-driven transaction-manager="transactionManager"  />
	<!--事務配置 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
</beans>

spring/mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations">
			 <list>
                <value>classpath:sql/*.xml</value>
            </list>
		</property>
	</bean>

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.sc.mapper" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
	
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  		<constructor-arg index="0" ref="sqlSessionFactory" />
	</bean>
</beans>

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-4.1.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
		
	<!-- 自動掃描,只掃描Controller -->
    <!-- <context:component-scan base-package="com.sc"></context:component-scan>	 -->
    <!-- 自動掃描,只掃描Controller -->
    <context:component-scan base-package="com.sc">
       <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
    </context:component-scan>	
    
    <!-- 啟用spring mvc 註解 -->
    <mvc:annotation-driven>
		<mvc:message-converters register-defaults="true">
			<!-- 將StringHttpMessageConverter的預設編碼設為UTF-8 -->
			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
		    	<constructor-arg value="UTF-8" />
			</bean>
			<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>  
			<!-- 將Jackson2HttpMessageConverter的預設格式化輸出設為true -->
			<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="prettyPrint" value="true"/>
            </bean>			
  		</mvc:message-converters>
	</mvc:annotation-driven>
	
	<!-- 靜態資源 -->
	<mvc:resources mapping="/static/**" location="/static/" />
	<mvc:resources mapping="/**" location="/" />
    
    <!-- 檢視解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="prefix" value="/WEB-INF/views"></property>
    	<property name="suffix" value=".jsp"></property>
    	<property name="order" value="1"></property>
    </bean>
    
    
</beans>

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

 <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xsi:schemaLocation="http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
	<description>Spring RabbitMQ 路由模式(Routing)配置</description>
	
	<!--引入配置屬性檔案 -->
	<context:property-placeholder location="classpath:config/*.properties" />
	
 	<!-- 配置RabbitMQ連線 -->
 	<!-- channel-cache-size,channel的快取數量,預設值為25 -->
 	<!-- cache-mode,快取連線模式,預設值為CHANNEL(單個connection連線,連線之後關閉,自動銷燬) -->
 	<rabbit:connection-factory id="connectionFactory" host="${rmq.host}" port="${rmq.port}"
 		username="${rmq.user}" password="${rmq.password}" virtual-host="${rmq.virtual}" channel-cache-size="${rmq.channelCacheSize}" cache-mode="CHANNEL"/>
 	<rabbit:admin connection-factory="connectionFactory"/>
 
 	<!--
 		定義訊息佇列
 		durable:是否持久化,如果想在RabbitMQ退出或崩潰的時候,不失去queue和訊息,需要同時標誌佇列(queue)
 		和交換機(exchange)持久化,即rabbit:queue標籤和rabbit:direct-exchange中的durable=true,而訊息(message)
 		預設是持久化的,可以看類org.springframework.amqp.core.MessageProperties中的屬性
 		public static final MessageDeliveryMode DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;
 		auto_delete: 當所有消費客戶端連線斷開後,是否自動刪除佇列 
 		exclusive: 僅建立者可以使用的私有佇列,斷開後自動刪除;
 		
 	-->
 	<rabbit:queue id="redpackqueue" name="SpringRabbit-RedPack-Queue" durable="true" auto-delete="false" exclusive="false">
 		<rabbit:queue-arguments>
 			<entry key="x-expires">
           		<value type="java.lang.Integer">10000</value>
        	</entry>
 		</rabbit:queue-arguments>
 	</rabbit:queue>
 	
 	<!--
 		繫結佇列
 		rabbitmq的exchangeType常用的三種模式:direct,fanout,topic三種,此處為direct模式,即rabbit:direct-exchange標籤,
 		Direct交換器很簡單,如果是Direct型別,就會將訊息中的RoutingKey與該Exchange關聯的所有Binding中的BindingKey進行比較,如果相等,
 		則傳送到該Binding對應的Queue中。有一個需要注意的地方:如果找不到指定的exchange,就會報錯。
 		但routing key找不到的話,不會報錯,這條訊息會直接丟失,所以此處要小心,
 	    auto-delete:自動刪除,如果為Yes,則該交換機所有佇列queue刪除後,自動刪除交換機,預設為false 
 	-->
 	<rabbit:direct-exchange name="SpringRabbit-RedPack-Exchange" durable="true" auto-declare="true" auto-delete="false">
 		<rabbit:bindings>
 			<rabbit:binding queue="redpackqueue" key="info"></rabbit:binding>
 		</rabbit:bindings>
 	</rabbit:direct-exchange>
 	
 	<!-- spring template宣告 -->
 	<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" exchange="SpringRabbit-RedPack-Exchange"></rabbit:template>

	<!-- 消費者  -->
 	<bean id="consumer" class="com.sc.consumer.RedPackConsumer"/>
	<!-- 佇列監聽-->
	<!-- acknowledge:auto 自動確認(預設), manual手動確認  -->
	<!-- concurrency:併發數量 ,設定的是對每個listener在初始化的時候設定的併發消費者的個數-->
	<!-- prefetch 是每次從一次性從broker裡面取的待消費的訊息的個數 -->
 	<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="5" prefetch="5">
 		<rabbit:listener queues="redpackqueue" ref="consumer" />
 	</rabbit:listener-container>
 	
</beans>

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<T> {

	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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sc.mapper.UserRedpackMapper" >
  <resultMap id="BaseResultMap" type="com.sc.entity.UserRedpack" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="userId" property="userid" jdbcType="VARCHAR" />
    <result column="redpackId" property="redpackid" jdbcType="INTEGER" />
    <result column="amount" property="amount" jdbcType="DECIMAL" />
    <result column="grabDate" property="grabdate" jdbcType="TIMESTAMP" />
    <result column="version" property="version" jdbcType="INTEGER" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, userId, redpackId, amount, grabDate, version
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from tb_user_redpack
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from tb_user_redpack
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.sc.entity.UserRedpack" >
    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>
  <insert id="insertSelective" parameterType="com.sc.entity.UserRedpack" >
    insert into tb_user_redpack
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="userid != null" >
        userId,
      </if>
      <if test="redpackid != null" >
        redpackId,
      </if>
      <if test="amount != null" >
        amount,
      </if>
      <if test="grabdate != null" >
        grabDate,
      </if>
      <if test="version != null" >
        version,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="userid != null" >
        #{userid,jdbcType=VARCHAR},
      </if>
      <if test="redpackid != null" >
        #{redpackid,jdbcType=INTEGER},
      </if>
      <if test="amount != null" >
        #{amount,jdbcType=DECIMAL},
      </if>
      <if test="grabdate != null" >
        #{grabdate,jdbcType=TIMESTAMP},
      </if>
      <if test="version != null" >
        #{version,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.sc.entity.UserRedpack" >
    update tb_user_redpack
    <set >
      <if test="userid != null" >
        userId = #{userid,jdbcType=VARCHAR},
      </if>
      <if test="redpackid != null" >
        redpackId = #{redpackid,jdbcType=INTEGER},
      </if>
      <if test="amount != null" >
        amount = #{amount,jdbcType=DECIMAL},
      </if>
      <if test="grabdate != null" >
        grabDate = #{grabdate,jdbcType=TIMESTAMP},
      </if>
      <if test="version != null" >
        version = #{version,jdbcType=INTEGER},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.sc.entity.UserRedpack" >
    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}
  </update>
</mapper>

src/main/resources/sql/RedpackMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sc.mapper.RedpackMapper" >
  <resultMap id="BaseResultMap" type="com.sc.entity.Redpack" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="userId" property="userid" jdbcType="VARCHAR" />
    <result column="amount" property="amount" jdbcType="DECIMAL" />
    <result column="unitAmount" property="unitamount" jdbcType="DECIMAL" />
    <result column="total" property="total" jdbcType="INTEGER" />
    <result column="remain" property="remain" jdbcType="INTEGER" />
    <result column="sendDate" property="senddate" jdbcType="TIMESTAMP" />
    <result column="version" property="version" jdbcType="INTEGER" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, userId, amount, unitAmount, total, remain, sendDate, version
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from tb_redpack
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from tb_redpack
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.sc.entity.Redpack" >
    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>
  <insert id="insertSelective" parameterType="com.sc.entity.Redpack" >
    insert into tb_redpack
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="userid != null" >
        userId,
      </if>
      <if test="amount != null" >
        amount,
      </if>
      <if test="unitamount != null" >
        unitAmount,
      </if>
      <if test="total != null" >
        total,
      </if>
      <if test="remain != null" >
        remain,
      </if>
      <if test="senddate != null" >
        sendDate,
      </if>
      <if test="version != null" >
        version,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="userid != null" >
        #{userid,jdbcType=VARCHAR},
      </if>
      <if test="amount != null" >
        #{amount,jdbcType=DECIMAL},
      </if>
      <if test="unitamount != null" >
        #{unitamount,jdbcType=DECIMAL},
      </if>
      <if test="total != null" >
        #{total,jdbcType=INTEGER},
      </if>
      <if test="remain != null" >
        #{remain,jdbcType=INTEGER},
      </if>
      <if test="senddate != null" >
        #{senddate,jdbcType=TIMESTAMP},
      </if>
      <if test="version != null" >
        #{version,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.sc.entity.Redpack" >
    update tb_redpack
    <set >
      <if test="userid != null" >
        userId = #{userid,jdbcType=VARCHAR},
      </if>
      <if test="amount != null" >
        amount = #{amount,jdbcType=DECIMAL},
      </if>
      <if test="unitamount != null" >
        unitAmount = #{unitamount,jdbcType=DECIMAL},
      </if>
      <if test="total != null" >
        total = #{total,jdbcType=INTEGER},
      </if>
      <if test="remain != null" >
        remain = #{remain,jdbcType=INTEGER},
      </if>
      <if test="senddate != null" >
        sendDate = #{senddate,jdbcType=TIMESTAMP},
      </if>
      <if test="version != null" >
        version = #{version,jdbcType=INTEGER},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.sc.entity.Redpack" >
    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>
  <!-- 查詢紅包剩餘個數 -->
  <select id="selectRemainByPrimaryKey" resultType="java.lang.Integer" parameterType="java.lang.Integer">
    select remain
    from tb_redpack
    where id = #{id,jdbcType=INTEGER}
  </select>
  <!-- 扣減紅包剩餘個數 -->
  <update id="updateRemainRedPack" parameterType="java.lang.Integer">
  	update tb_redpack set remain = remain - 1
  	where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

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<String> grab(String userid){
		try {
			commonMqService.sendGradRedPack(userid);
			return new ResultModel<String>(0, userid, "搶紅包成功");
		} catch (Exception e) {
			e.printStackTrace();
			log.error("使用者:"+userid+" 搶紅包失敗");
			return new ResultModel<String>(0, userid, "搶紅包失敗");
		}
	}

}

13.瀏覽器JS模擬消費者請求搶紅包
這裡模擬了1000個消費者搶紅包。為了更好的測試可以提高併發量

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%
	String path = request.getContextPath();
%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="<%=path%>/static/js/jquery-1.9.1.js"></script>
</head>
<body>
	<button onclick="test(2000)">搶紅包測試</button>
</body>
<script type="text/javascript">
	function test(usernum){
		console.log("usernum:"+usernum);
		for(var i = 0; i < usernum; i++){
			console.log("i:"+i);
			$.post('<%=path%>/redpack/grab', {'userid':i},function(data){
				console.log(data);
			})
		}
	}
</script>
</html>

14.執行結果檢視是否插入了1000條使用者搶紅包記錄,結果如下,證明執行OK
在高併發量的情況下可能會出現超發的情況,這就需要後臺加讀寫鎖或者結合redis處理。本文就不做詳細講解,有時間後續在進一個深入。
在這裡插入圖片描述