抽奖系统的流量削峰方案

如果观看抽奖或秒杀系统的请求监控曲线,你就会发现这类系统在活动开放的时间段内会出现一个波峰,而在活动未开放时,系统的请求量、机器负载一般都是比较平稳的。为了节省机器资源,我们不可能时时都提供最大化的资源能力来支持短时间的高峰请求。所以需要使用一些技术手段,来削弱瞬时的请求高峰,让系统吞吐量在高峰请求下保持可控。

​​

最近在做一个小型的抽奖系统,用户中奖之后需要调用转账接口进行虚拟金的转账。转账接口有频控的逻辑,因此不能把抽奖瞬间的大量请求都发往转账系统,必须对请求进行削峰。削峰的方式有很多种,下面就来简单地聊一下。

请求排队

削峰最常用的一种方式是请求排队。瞬时的请求量太大,那么就把这些请求先排队存起来,再依据系统所能提供的消费能力按需消费。在量小的时候,抽奖与发货这两个动作可以是同步的(如下左图),这是一种紧耦合系统,SVR B的处理能力必须跟得上SVR A的处理能力。当SVR A 与SVR B 存在处理能力差异时,可以引入消息队列,把对服务的同步调用转化成对队列的异步消费。

​​

可以用来作为队列的工具有很多,典型的如Message Queue消息队列,也可以利用数据库Mysql或是Redis来实现分布式队列,跟进业务场景来自行进行选择。例如,我在实现抽奖系统的时候,使用的是Mysql,原因是SVR A已经把用户的抽奖信息落地到的数据库,那么SVR B就可以利用Mysql作为一个队列,来达到按能力消费的需求。

Mysql

用户中奖的时候,SVR A 会将用户中奖信息写到数据库中。SVR B按照自己的消费能力,从数据库中把数据select出来执行转账的逻辑。数据库表中的每一行记录,都可以看作是一个等待被消费的消息。如何保证消息按序(正序或倒序)消费?可以利用update_time 来标记消息入队时间,设定update_time字段:

 update_time timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP  DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间'

必须使用一个字段来标记某行记录的消费状态。消费过的消息不必再select出来处理。另外,在有多个消息消费者的时候(比如有多个线程来消费数据库中的这些中奖信息时),需要保证消息不会重复被消费。可以使用二段式提交的方式来保证。以字段present_flag来表示消费状态,present_flag有三个取值: 0:中奖,未转账 1:一阶段提交(即准备转账) 2:二阶段提交(转账完成)

对于SVR B ,需要进行如下的操作: 步骤一:将数据库中present_flag 为0 的记录按序捞取出来,这里可以批量拉取,比如一次拉取100条记录 步骤二:按序处理每笔中奖记录的转账逻辑,调用转账接口之前,将present_flag设置为1,sql中的条件是present_flag为0; 步骤三:执行转账逻辑 步骤四:转账成功,将present_flag设置为2,sql中条件是present_flag为1。

这样即使同一行记录被多个消费者拉取出来,也能保证只有一个能够成功执行步骤三。转账失败(消费失败) 的记录如何处理?可以使用一个定时脚本将present_flag为1的update成present_flag为0,再次进行消费。

通过这种异步消费的方式,来保证中奖记录慢慢被消费完。这种方式在极端的情况下,比如刚刚执行完步骤三 机器就挂掉了,那么可能会出现重复消费的情况。根据业务对重复消费的容忍度来进行选择。

Redis

Redis的list数据结构提供了BLPOP和BRPOP,表示列表的阻塞式弹出。BLPOP的BRPOP的区别仅仅在取元素的位置不同。使用方式为:

BRPOP key timeout

当给定的列表内没有任何元素可供弹出的时候,连接将被阻塞,直到等待超时或发现可弹出的元素为止,超时参数 timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长。相同的key可以被多个客户端同时阻塞,不同的客户端会被放进一个队列中,按照【先阻塞先服务】的顺序为key执行BRPOP 命令。利用这个特点,可以来实现一个轻量级的消息队列服务。

消息队列组件

例如kafka、ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ、RocketMQ等消息队列,本就是为异步化消息消费、应用解耦、流量消费而设计。业务根据需求加以选型即可。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏信安之路

【读者投稿】wifi渗透-狸猫换太子

上期作者发布了一篇关于wifi钓鱼的方法,今天我来给大佬们带来一篇关于拿到wifi密码能干什么?

1360
来自专栏CSDN技术头条

【问底】许鹏:使用Spark+Cassandra打造高性能数据分析平台(一)

【导读】笔者(许鹏)看Spark源码的时间不长,记笔记的初衷只是为了不至于日后遗忘。在源码阅读的过程中秉持着一种非常简单的思维模式,就是努力去寻找一条贯穿全局的...

2988
来自专栏影子

给Ionic写一个cordova(PhoneGap)插件

47410
来自专栏FreeBuf

关于C/S架构系统的安全监测

由于工作需求,需要对一大批C/S架构的系统进行测试,所以这几天一直在摸索怎么个套路法,踩过的坑就不发了,直接奔我个人的套路: C/S架构的系统,说最直白一点就是...

3998
来自专栏杨建荣的学习笔记

物化视图实现的特殊数据复制(r11笔记第42天)

今天开发同事碰到一个有些复杂的数据复制需求,想让我帮忙看看能否实现,当然猛一听需求是不可能实现的。不过还是耐着性子和他们讨论了一下,不过我想了下,似乎还是有...

3185
来自专栏GopherCoder

专栏:010:SQL VS No SQL

1563
来自专栏流柯技术学院

NGINX引入线程池 性能提升9倍

正如我们所知,NGINX采用了异步、事件驱动的方法来处理连接。这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作...

1302
来自专栏数据和云

RAC一个节点自动重启问题分析

题记:在RAC数据库的故障当中,节点重启的现象很常见,在这种问题的处理当中,有一定的规律性。为了更好的说明这个问题的处理过程,保证出现该类问题的时候,能够有序的...

3685
来自专栏散尽浮华

分布式监控系统Zabbix-3.0.3-完整安装记录-新报微信报警(企业微信)

一般来说,Zabbix可以通过多种方式把告警信息发送到指定人,常用的有邮件,短信报警方式,但是现在越来越多的企业开始使用zabbix结合微信作为主要的告警方式,...

25810
来自专栏Python、Flask、Django

【转】python 相见恨晚的第三方类库

1542

扫码关注云+社区

领取腾讯云代金券