【实战问题】-- 高并发架构设计以及超领现象解决?

本文探讨了一个高并发环境中,用户领取礼品的系统设计。首先,介绍了查询用户是否已领取的流程,利用Redis缓存减少数据库压力,并提出使用布隆过滤器防止恶意攻击。接着,详细阐述了领取接口的处理,包括调用外部服务C,记录领取日志以及异常处理。面对可能出现的超领问题,提出了使用Redis的`SETNX`命令实现分布式锁来避免并发领取。最后,指出方案可能存在的坑,并预告后续讨论。
摘要由CSDN通过智能技术生成

现在 有一个场景,领取礼品,每个用户有次数限制,用户通过前端点击,调用了应用A的接口,里面调用了服务B,服务B里面去调用了服务C,注意服务C是其他部门的服务。服务C负责真正的发放礼品。(假设这个服务C我们是不可修改的,A,B是自己团队负责的,并且可能出现高并发的情况)

我们应该如何做这个次数限制呢?

假设每次领取礼品的活动有一个activityId,一个用户一个活动可以领取一件礼品,礼品有giftId,不可以多领,每个用户对应一个uid

查询是否可以领取

首先对于前端而言,进入系统,首先需要获取用户是否已经领取过,而这个是否已经领取过,具体的实现我们应该写在B服务中,用户通过应用A,请求到服务B,返回用户是否已经领取的结果。

查询是否领取的流程大致如下:
用户进入页面,前端如果有缓存的话,可以为他展示之前缓存的结果,假设没有缓存,就会请求A应用,A应用会去请求B服务,B服务首先需要判断礼品或者活动是否存在。

去redis里面取活动或者礼品是否存在,如果redis没有查询到,那么就查询数据库,返回结果,如果数据库都没有,说明这个前端请求很可能是捏造的,直接返回结果“活动或者礼品不存在”,如果此时查询出来,确实存在,那么就需要去查询是否领取过,同样是查询redis,不存在的情况下,查询数据库,再返回结果。,如果领取过,则会有领取结果,前端将按键置灰,否者用户按键可以领取。

上面的redis肯定是需要我们维护的,这里不展开讲。比如增加活动的时候,除了改数据库,同时需要redis里面写一份数据,key可以是activityId_giftId,记录已经有的活动,用户成功领取的时候,同样是不仅增加数据库记录,也需要往redis写一份数据,key可以是activityId_giftId_uid,记录该用户已经领取过该活动的奖品。

但是上面的系统,有一个问题,就是活动/礼品不存在的时候,请求会每一次都直接打到数据库,如果是恶意攻击,数据库就挂了。这里当然可以考虑使用布隆过滤器,对请求参数中的活动/礼品做过滤,同时也可以考虑其他的防爬虫手段,比如滑动窗口统计请求数,根据ip,客户端id,uid等等。

当然,如果可以保证redis数据可靠,稳定,可以不请求数据库,redis不包含则说明不存在,直接返回。但是这种做法需要在增加活动/修改商品的时候,同时将redis一同修改同步。如果redis挂掉的情况,或者请求redis异常,再去查询数据库。如果能接受修改数据库活动信息不立马更新,也可以考虑更新完数据库,用消息队列发一条消息,收到再做redis更新。当然,这个不是一种好的做法,解耦合之后,增加了复杂度。前面说的做法,只要redis挂了,数据库理论上也支撑不了多久(极端情况)。

(当然,上面不是完美的方案,是个大致流程)

领取礼品接口怎么处理?

首先流程上与上面的查询是否领取过有些类似,,但是在查询是否领取过这一步之后,有所不同。如果已经领取过,则直接返回,但是如果没有领取过,需要调用C服务进行领取,如果调用C接口失败,或者返回领取失败,B服务需要做的事,就是记录日志或者告警,同时返回失败。
如果C服务返回领取成功,那么需要记录领取记录到数据库,并且更新缓存,表示已经领取过该礼品,这也是上面为什么一般能直接查询缓存就可以知道用户是否领取过的原因。

这个设计中,其实C服务才是真正实现方法奖品的服务,我们做的A和B相当于调用别人的服务,做了中间服务,这种情况更需要记录日志,控制爬虫,恶意攻击等等,同时做好异常处理。

上面的设计,如果我们来写段伪代码,来看看有什么问题?

    public String 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值