前些天上线的扫码送会员活动。
场景:用户登陆帐号以后,扫二维码,送七天黄金会员,限制每一个账号只能领取一个
有恶意用户刷接口,在高并发下越过限制。redis
这个流程在通常环境下是没有问题的,在高并发下就不行了。领取会员流程: 1.后端先生成卡卷,将卡号放到消息队列中 2.用户扫码请求领取会员接口 2-1).先检查用户是否已经领取过该活动会员 2-2).领取过return “该账号已领取”的标示 2-3).没领取从消息队列中拿取一张卡号 2-4).激活卡 2-5).更新用户本次活动为已经激活
2-1) 2-2) 2-3) 2-4) 2-5) 线程a --> 线程b --> 线程c -->
高并发下模拟几个线程同时请求后端
如今的rpc服务,除去极其敏感性数据的操做,其它数据的接口基本都没有作数据一致性控制。api
其实作了控制也不能解决这个问题。再来讲这个问题,高并发下由于线程a已经执行完激活卡的操做,用户的会员已经创建权益。但这时候线程a尚未执行到2-5,还没更新用户的领取卡卷的状态,这时候,又有一个这个用户的领取卡卷请求过来。2-1的check 操做并不能阻止这个请求,一样的再次领取卡卷而且激活,致使线程a在的执行在2-1到2-5之间都会有其它的线程越过检查。并发
解决这种并发问题无非是两种,悲观锁和乐观锁。
悲观锁阻塞,乐观锁快速响应失败。高并发
优势 缺点 悲观锁 能够响应重复请求,幂等 高并发下请求堆积 乐观锁 高并发下没有大量线程阻塞 不可重复响应,不幂等
考虑并发量比较大,采用的乐观锁实现。对流程进行加锁。.net
2种实现方式:redis和MySQL,考虑下在不修改原表的状况下,使用redis的SETNX的api线程
实现:code
2-0).活动-账号造成key,SETNX(key)成功返回1,失败返回0 只有返回1,才能进行后续流程,将并发控制交给redis,redis是线程模型没有并发问题 2-1).先检查用户是否已经领取过该活动会员 2-2).领取过return “该账号已领取”的标示 2-3).没领取从消息队列中拿取一张卡号 2-4).激活卡 2-5).更新用户本次活动为已经激活 2-6).将删除活动-账号造成的key 2-0) 2-1) 2-2) 2-3) 2-4) 2-5) 2-6) 线程a ->1 线程b ->0 <- 线程c ->0 <- 只有线程a已经执行过2-6,才能线程b进入流程,可是这时候用户已经为领取过卡卷状态 线程a -> 线程b ->1 用户卡卷已经更新过 线程c ->0