高并发下接口的并发问题

Posted 爱畔畔真是太好了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高并发下接口的并发问题相关的知识,希望对你有一定的参考价值。

事故

前些天上线的扫码送会员活动。
场景:用户登录账号之后,扫二维码,送七天黄金会员,限制每个帐号只能领取一个
有恶意用户刷接口,在高并发下越过限制。

原因

领取会员流程:
    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服务,除去极其敏感性数据的操作,其它数据的接口基本都没有做数据一致性控制。

其实做了控制也不能解决这个问题。再来说这个问题,高并发下因为线程a已经执行完激活卡的操作,用户的会员已经建立权益。但这时候线程a还没有执行到2-5,还没更新用户的领取卡卷的状态,这时候,又有一个这个用户的领取卡卷请求过来。2-1的check 操作并不能阻止这个请求,同样的再次领取卡卷并且激活,导致线程a在的执行在2-1到2-5之间都会有其它的线程越过检查。

解决

解决这种并发问题无非是两种,悲观锁和乐观锁。
悲观锁阻塞,乐观锁快速响应失败。

                优点                      缺点

悲观锁     可以响应重复请求,幂等         高并发下请求堆积


乐观锁     高并发下没有大量线程阻塞        不可重复响应,不幂等

考虑并发量比较大,采用的乐观锁实现。对流程进行加锁。

2种实现方式:redis和MySQL,考虑下在不修改原表的情况下,使用redis的SETNX的api

实现:

        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                                            
 

以上是关于高并发下接口的并发问题的主要内容,如果未能解决你的问题,请参考以下文章

高并发下接口幂等性的解决方案

java里,如何保证高并发下的数据安全

3高并发下Nginx优化

高并发下springcloud hystrix的严重问题?

redis实现高并发下的抢购/秒杀功能

【golang】高并发下TCP常见问题解决方案