目录
一、秒杀系统需求分析
1-1 超卖问题
10个物品被20个用户抢到。
查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数(线程安全)
解决思路:解决线程安全。
方式一、因为列表的队列操作是原子的,即使有很多用户同时到达,也是依次执行的。(lpop)
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)
# 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 '很遗憾,抢购已经结束。'
三、Jmeter 并发测试工具
3-1 Jmeter 汉化
3-2 随机参数(函数)
3-3 响应编码问题(Unicode 转换)
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));
重启生效:sampleresult.default.encoding=UTF-8