电商抢购高并发JAVA简单实现

假设场景如下:

库存20瓶茅台53°。

页面发起100万次请求抢购20瓶茅台,用Jmeter测试工具模拟。

要求100万次请求全部得到及时响应,抢购到或者抢购不到。

单机springboot内嵌tomcat,系统服务正常运行,不宕机。

库存不能产生超卖,数据库的库存不能出问题。

 

Demo设计思路:

1 限流 每秒只放5个请求,其他请求直接返回:本次未抢购到。不走后续业务逻辑。

2 缓存 每次用户请求茅台的剩余库存数时,查询jvm缓存中的剩余数量。

 

说白了,上谷歌的guava包做。maven依赖引入

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

mysql表设计

1 用户购买记录表

CREATE TABLE `buy_goods` (
  `buyer` varchar(255) DEFAULT NULL,
  `sku_name` varchar(255) DEFAULT NULL,
  `buydate` datetime DEFAULT NULL,
  `amout` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2 库存信息表

CREATE TABLE `sku_stock` (
  `sku_name` varchar(255) DEFAULT NULL,
  `avaliable_amout` bigint(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入20瓶茅台的一条记录。

INSERT INTO `demoa`.`sku_stock` (`sku_name`, `avaliable_amout`) VALUES ('maotai', '20');

 

springboot程序。

BuyDao.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.dao.IBuyDao">
    <!--查询单个-->
    <insert id="insertBuyGoods">
       INSERT INTO buy_goods (
        `buyer`,
        `sku_name`,
        `buydate`,
        `amout`
        )
        VALUES
        (
            #{param1},
            #{param2},
            sysdate(),
            #{param3}
        )

    </insert>

    <select id="querySkuAmout" parameterType="java.lang.String" resultType="java.lang.Long">
       SELECT
            avaliable_amout
        FROM
            sku_stock
        WHERE
            sku_name =#{param1}
    </select>

    <update id="updateSkuAmout">
        update sku_stock
        set avaliable_amout=avaliable_amout-#{param2}
        where sku_name =#{param1}
    </update>


</mapper>

IBuyDao接口:
package com.dao;


public interface IBuyDao {
    int insertBuyGoods(String buyer,String skuname,int amout);
    long querySkuAmout(String skuname);
    int updateSkuAmout(String skuname,int reduceAmount);
}

 

BuyDemo。Controller:

package com;

import com.dao.IBuyDao;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@Slf4j
public class BuyDemo {

    @Resource
    IBuyDao buyDao;

    final AtomicLong atomicLong=new AtomicLong(0);

    final AtomicInteger atomicInteger=new AtomicInteger(0);

    public static final SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    //流控速率,每秒放行5个请求。
    final RateLimiter rateLimiter=RateLimiter.create(5);

    //guava_cache本地缓存,JVM级别缓存
    volatile LoadingCache<String, Long> Cache_AvaliableAmout = CacheBuilder.newBuilder()
            //设置cache的初始大小为10,要合理设置该值
            .initialCapacity(10)
            //设置并发数为3,即同一时间最多只能有3个线程往cache执行写入操作
            .concurrencyLevel(3)
            //设置cache中的数据在写入之后的存活时间为10秒
            .expireAfterWrite(10, TimeUnit.SECONDS)
            //构建cache实例
            .build(
                    new CacheLoader<String, Long>() {
                        @Override
                        public Long load(String skuName) throws Exception {
                            long amout=  buyDao.querySkuAmout(skuName);
                            log.info("查询数据库:"+skuName+"可用数量为:"+amout);
                            return amout;
                        }
                    }
            );

    @RequestMapping("/buygoods")
    @ResponseBody
    public String buygoods(String skuName) {
        //是否能通过流控,true表示该请求通过流控,可以请求后台业务,false表示该请求被流控,无法请求业务,直接被返回未抢购到
        boolean get=rateLimiter.tryAcquire();
        //被流控,抢购失败
        if(!get)
        {
            return "buying maotai is too hot,wait later "+atomicLong.incrementAndGet();
        }
        try {
            //商品卖完了,直接返回无货。
           if(Cache_AvaliableAmout.get(skuName)<=0)
           {
               return "no avaliable sku  "+atomicLong.incrementAndGet();
           }
           //简单的锁机制
           synchronized (this)
           {
               //double-check防止过量超卖
               if(Cache_AvaliableAmout.get(skuName)<=0)
               {

                   return "no avaliable sku "+atomicLong.incrementAndGet();
               }
               //更新库存数量,插入购买者的购买记录。
               buyDao.updateSkuAmout(skuName,1);
               buyDao.insertBuyGoods(UUID.randomUUID().toString(),skuName,1);
               //刷新缓存
               Cache_AvaliableAmout.refresh(skuName);
               log.info(simpleDateFormat.format(new Date())+" buy one "+atomicInteger.incrementAndGet());
           }
           return "buy success";
        } catch (Exception e) {
            e.printStackTrace();
            return "system error";
        }
    }
}

Jmeter测试信息:

 

点击运行后:

Jemter输出信息:

 

 

Jmeter一百万请求全部完成。

后续因为无货,请求不走mysql,请求秒回。

前期有货,抢购到需要走sql查询和更新,所以有点耗时,几百毫秒。

 

idea控制台输出信息:

2021-05-16 20:53:20.657  INFO 23664 --- [http-nio-8900-exec-88] com.BuyDemo                              : 查询数据库:maotai可用数量为:20
2021-05-16 20:53:20.680  INFO 23664 --- [http-nio-8900-exec-88] com.BuyDemo                              : 查询数据库:maotai可用数量为:19
2021-05-16 20:53:20.722  INFO 23664 --- [http-nio-8900-exec-88] com.BuyDemo                              : 2021-05-16 20:53:20.722 buy one 1
2021-05-16 20:53:20.753  INFO 23664 --- [http-nio-8900-exec-130] com.BuyDemo                              : 查询数据库:maotai可用数量为:18
2021-05-16 20:53:20.843  INFO 23664 --- [http-nio-8900-exec-130] com.BuyDemo                              : 2021-05-16 20:53:20.753 buy one 2
2021-05-16 20:53:20.861  INFO 23664 --- [http-nio-8900-exec-66] com.BuyDemo                              : 查询数据库:maotai可用数量为:17
2021-05-16 20:53:20.862  INFO 23664 --- [http-nio-8900-exec-66] com.BuyDemo                              : 2021-05-16 20:53:20.862 buy one 3
2021-05-16 20:53:20.882  INFO 23664 --- [http-nio-8900-exec-41] com.BuyDemo                              : 查询数据库:maotai可用数量为:16
2021-05-16 20:53:20.883  INFO 23664 --- [http-nio-8900-exec-41] com.BuyDemo                              : 2021-05-16 20:53:20.883 buy one 4
2021-05-16 20:53:20.899  INFO 23664 --- [http-nio-8900-exec-102] com.BuyDemo                              : 查询数据库:maotai可用数量为:15
2021-05-16 20:53:20.900  INFO 23664 --- [http-nio-8900-exec-102] com.BuyDemo                              : 2021-05-16 20:53:20.899 buy one 5
2021-05-16 20:53:20.922  INFO 23664 --- [http-nio-8900-exec-65] com.BuyDemo                              : 查询数据库:maotai可用数量为:14
2021-05-16 20:53:20.922  INFO 23664 --- [http-nio-8900-exec-65] com.BuyDemo                              : 2021-05-16 20:53:20.922 buy one 6
2021-05-16 20:53:20.941  INFO 23664 --- [http-nio-8900-exec-181] com.BuyDemo                              : 查询数据库:maotai可用数量为:13
2021-05-16 20:53:20.941  INFO 23664 --- [http-nio-8900-exec-181] com.BuyDemo                              : 2021-05-16 20:53:20.941 buy one 7
2021-05-16 20:53:20.961  INFO 23664 --- [http-nio-8900-exec-97] com.BuyDemo                              : 查询数据库:maotai可用数量为:12
2021-05-16 20:53:20.962  INFO 23664 --- [http-nio-8900-exec-97] com.BuyDemo                              : 2021-05-16 20:53:20.962 buy one 8
2021-05-16 20:53:21.181  INFO 23664 --- [http-nio-8900-exec-119] com.BuyDemo                              : 查询数据库:maotai可用数量为:11
2021-05-16 20:53:21.182  INFO 23664 --- [http-nio-8900-exec-119] com.BuyDemo                              : 2021-05-16 20:53:21.182 buy one 9
2021-05-16 20:53:21.194  INFO 23664 --- [http-nio-8900-exec-113] com.BuyDemo                              : 查询数据库:maotai可用数量为:10
2021-05-16 20:53:21.195  INFO 23664 --- [http-nio-8900-exec-113] com.BuyDemo                              : 2021-05-16 20:53:21.194 buy one 10
2021-05-16 20:53:21.209  INFO 23664 --- [http-nio-8900-exec-61] com.BuyDemo                              : 查询数据库:maotai可用数量为:9
2021-05-16 20:53:21.210  INFO 23664 --- [http-nio-8900-exec-61] com.BuyDemo                              : 2021-05-16 20:53:21.210 buy one 11
2021-05-16 20:53:21.225  INFO 23664 --- [http-nio-8900-exec-115] com.BuyDemo                              : 查询数据库:maotai可用数量为:8
2021-05-16 20:53:21.225  INFO 23664 --- [http-nio-8900-exec-115] com.BuyDemo                              : 2021-05-16 20:53:21.225 buy one 12
2021-05-16 20:53:21.251  INFO 23664 --- [http-nio-8900-exec-98] com.BuyDemo                              : 查询数据库:maotai可用数量为:7
2021-05-16 20:53:21.253  INFO 23664 --- [http-nio-8900-exec-98] com.BuyDemo                              : 2021-05-16 20:53:21.253 buy one 13
2021-05-16 20:53:21.267  INFO 23664 --- [http-nio-8900-exec-94] com.BuyDemo                              : 查询数据库:maotai可用数量为:6
2021-05-16 20:53:21.268  INFO 23664 --- [http-nio-8900-exec-94] com.BuyDemo                              : 2021-05-16 20:53:21.268 buy one 14
2021-05-16 20:53:21.285  INFO 23664 --- [http-nio-8900-exec-116] com.BuyDemo                              : 查询数据库:maotai可用数量为:5
2021-05-16 20:53:21.286  INFO 23664 --- [http-nio-8900-exec-116] com.BuyDemo                              : 2021-05-16 20:53:21.286 buy one 15
2021-05-16 20:53:21.299  INFO 23664 --- [http-nio-8900-exec-147] com.BuyDemo                              : 查询数据库:maotai可用数量为:4
2021-05-16 20:53:21.300  INFO 23664 --- [http-nio-8900-exec-147] com.BuyDemo                              : 2021-05-16 20:53:21.300 buy one 16
2021-05-16 20:53:21.315  INFO 23664 --- [http-nio-8900-exec-38] com.BuyDemo                              : 查询数据库:maotai可用数量为:3
2021-05-16 20:53:21.315  INFO 23664 --- [http-nio-8900-exec-38] com.BuyDemo                              : 2021-05-16 20:53:21.315 buy one 17
2021-05-16 20:53:21.335  INFO 23664 --- [http-nio-8900-exec-20] com.BuyDemo                              : 查询数据库:maotai可用数量为:2
2021-05-16 20:53:21.335  INFO 23664 --- [http-nio-8900-exec-20] com.BuyDemo                              : 2021-05-16 20:53:21.335 buy one 18
2021-05-16 20:53:21.515  INFO 23664 --- [http-nio-8900-exec-3] com.BuyDemo                              : 查询数据库:maotai可用数量为:1
2021-05-16 20:53:21.516  INFO 23664 --- [http-nio-8900-exec-3] com.BuyDemo                              : 2021-05-16 20:53:21.516 buy one 19
2021-05-16 20:53:21.711  INFO 23664 --- [http-nio-8900-exec-159] com.BuyDemo                              : 查询数据库:maotai可用数量为:0
2021-05-16 20:53:21.712  INFO 23664 --- [http-nio-8900-exec-159] com.BuyDemo                              : 2021-05-16 20:53:21.712 buy one 20
2021-05-16 20:53:31.915  INFO 23664 --- [http-nio-8900-exec-127] com.BuyDemo                              : 查询数据库:maotai可用数量为:0
2021-05-16 20:53:42.101  INFO 23664 --- [http-nio-8900-exec-191] com.BuyDemo                              : 查询数据库:maotai可用数量为:0
2021-05-16 20:53:52.300  INFO 23664 --- [http-nio-8900-exec-117] com.BuyDemo                              : 查询数据库:maotai可用数量为:0

mysql表信息:

1 库存表茅台卖为0

2 用户购买记录表,共有20条购买记录

 

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值