Flask - 抢购、秒杀系统

时间:2024-03-17 12:04:21

目录

一、秒杀系统需求分析

1-1 超卖问题

1-2 超抢问题

二、基于 Redis 队列实现抢购

2-0 思路总结

2-1 初始版本

三、Jmeter 并发测试工具

3-1 Jmeter 汉化

3-2 随机参数(函数)

3-3 响应编码问题(Unicode 转换)


一、秒杀系统需求分析

1-1 超卖问题

10个物品被20个用户抢到。

查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数(线程安全)

解决思路:解决线程安全。

方式一、因为列表的队列操作是原子的,即使有很多用户同时到达,也是依次执行的。(lpop)

php 实现参考

1-2 超抢问题

对于同一个物品,单用户能抢到多个。

解决思路:判断用户是否抢到,需要记录抢购用户

二、基于 Redis 队列实现抢购

使用redis队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行。

mysql事务在高并发下性能下降很厉害,文件锁的方式也是

并发思路:celery 异步任务(优化高并发场景) + redis 队列

2-0 思路总结

  • key - value []
  • 假设:商品A限购5个,初始化list在redis内
    • 抢购商品A - [1,1,1,1,1]
  • 开始抢购则进行pop操作,每来一个用户先判断list的长度是否非0,若非0,pop一个,并将用户名写入另一个list
    • 抢购商品A - [1,1,1,1]
    • 商品A已抢用户 - [用户1,]
  • 若list(抢购商品A )已经pop空,则将返回用户失败信息。

2-1 初始版本

# ---------用于生成抢购库存列表-----------------
li = [1 for i in range(50)]
print(li)
re = conn.lpush('goods_a', *li)
print(re)

 Flask - 抢购、秒杀系统

# API 接口,CBV
class Seckill(Resource):
    def post(self):
        # 获取form表单内的username,唯一参数
        username = request.form['username']
        # redis 内list存储数据为byte类型
        username = bytes(username, encoding='utf-8')

        # 获得当前redis内已购用户信息
        users_list = redis_store0.lrange('a_users', 0, -1)
        if username in users_list:
            return '您已经抢到了,请勿重复请求。'

        # 判断库存
        goods_count = redis_store0.llen('goods_a')
        if goods_count:
            res = redis_store0.lpop('goods_a')
            if res:
                redis_store0.lpush('a_users', username)
                return '恭喜您,您已抢到。'
        return '很遗憾,抢购已经结束。'

Flask - 抢购、秒杀系统

三、Jmeter 并发测试工具

jmeter 官方下载

使用参考博客

3-1 Jmeter 汉化

Flask - 抢购、秒杀系统

 Flask - 抢购、秒杀系统

Flask - 抢购、秒杀系统

3-2 随机参数(函数)

Flask - 抢购、秒杀系统

3-3 响应编码问题(Unicode 转换)

解决参考

Flask - 抢购、秒杀系统

Flask - 抢购、秒杀系统

Flask - 抢购、秒杀系统

private static String ascii2native ( String asciicode )
{
    String[] asciis = asciicode.split ("\\\\u");
    String nativeValue = asciis[0];
    try
    {
        for ( int i = 1; i < asciis.length; i++ )
        {
            String code = asciis[i];
            nativeValue += (char) Integer.parseInt (code.substring (0, 4), 16);
            if (code.length () > 4)
            {
                nativeValue += code.substring (4, code.length ());
            }
        }
    }
    catch (NumberFormatException e)
    {
        return asciicode;
    }
    return nativeValue;
}
String asciicode =new String(prev.getResponseData(),"UTF-8");
prev.setResponseData(ascii2native(asciicode));

Flask - 抢购、秒杀系统

重启生效:sampleresult.default.encoding=UTF-8

Flask - 抢购、秒杀系统