1.前言

抽奖是一个典型的高并发场景应用,平时流量不多,但遇到大促活动,流量就会暴增,今年的周年庆期间的日均UV就超过百万。在过去的一年里,负责过这个项目的多次重构工作,期间各种踩坑无数,就以此文当做总结,来聊聊我们是如何架构这个高并发系统吧。

2.整体设计详解

在我看来,能提高服务器应对并发的能力的方式无非两种:

  1. 限流削峰:通过降低实际抵达服务器的并发量,降低服务器处理压力;
  2. 性能优化:从前台到硬件,优化系统各方面性能,提高服务器处理能力。

接下来我们围绕这两个方面谈谈在1号店抽奖系统中所做的工作和遇到的坑。

整体架构如下图:

2.1服务器层的限流削峰

我们的负载服务器使用的是A10,商业的负载均衡硬件,相比nginx,虽然花不少钱,但在使用配置等方面简单,便于维护,web服务器自然是Tomcat。这里我们优化了两件事情。

1.前言

抽奖是一个典型的高并发场景应用,平时流量不多,但遇到大促活动,流量就会暴增,今年的周年庆期间的日均UV就超过百万。在过去的一年里,负责过这个项目的多次重构工作,期间各种踩坑无数,就以此文当做总结,来聊聊我们是如何架构这个高并发系统吧。

2.整体设计详解

在我看来,能提高服务器应对并发的能力的方式无非两种:

  1. 限流削峰:通过降低实际抵达服务器的并发量,降低服务器处理压力;
  2. 性能优化:从前台到硬件,优化系统各方面性能,提高服务器处理能力。

接下来我们围绕这两个方面谈谈在1号店抽奖系统中所做的工作和遇到的坑。

整体架构如下图:

2.1服务器层的限流削峰

我们的负载服务器使用的是A10,商业的负载均衡硬件,相比nginx,虽然花不少钱,但在使用配置等方面简单,便于维护,web服务器自然是Tomcat。这里我们优化了两件事情。

a).防cc

负载均衡作为分布式系统的第一层,本身并没有好说的。唯一值得一提的是针对此类大流量场景,我们特意引入了防cc机制,限制用户每分钟的最高访问次数,超出频率的请求直接拒绝,防止用户使用脚本等方式刷请求。这个在我们使用的负载均衡器A10上可以自行配置,如果是nginx也有限制连接模块可以使用,这也是流量削峰的第一层。

b).Tomcat并发参数

我们之前线上的Tomcat是使用默认的参数maxThreads=500,在流量没有上来之前没什么感觉,但大流量情景下会抛出不少异常日志。在通过性能压测后发现,在并发请求超出400+后,响应速度明显变慢,后台开始出现数据库,接口等链接超时,因此将maxThread改为了400,限制tomcat处理量,进一步削减流量。

2.2应用层的限流削峰

从这里开始,请求就进入应用代码中了,在这一层,我们可以通过代码来进行流量削峰工作了,主要包括信号量,用户行为识别等方式。

a).信号量

前面谈到了通过Tomcat并发线程配置来拦截超出的流量,但这里有一个问题是超出的请求要么被阻塞,要么被直接拒绝的,不会给出响应。在客户端看到的是长时间没有响应或者请求失败,然后不断重试,我们更希望在这个时候响应一些信息,比如说直接给出提示没有中奖,通知客户端不再请求,从而提高用户体验。因此在这里我们使用了java并发包中的Semaphore,伪代码如下:

semaphore=new Semaphore(350);if (!semaphore.tryAcquire()) {return "error";
}
try {execute();
} finally {semaphore.release();
}

由于通过压测得出的Tomcat最大线程数配置为400,这里的信号量我们设成了350,剩下50个线程用来响应超出的请求。在这种情景下,我们曾用800个并发做过测试,由于请求还未抵达复杂的业务逻辑中,客户端可以在10ms内收到错误响应,不会感到延迟或请求拒绝的现象。

b).用户行为识别

Tomcat及信号量进行的并发控制我称之为硬削峰,并不管用户是谁,超出设置上限直接拒绝。但我们更想做的是将非法的请求拦截掉,比如脚本,黄牛等等,从而保证正常用户的访问,因此,在公司风控等部门同学的协助下,引入一些简单的用户行为识别。

  1. 实时人机识别:在用户请求过程中,正常用户跟机器的请求数据会有差异,如果请求数据跟实际的数据不一致,通过实时的识别,自然就可以将这个请求标识为非法请求,直接拦截。

  2. 风控列表:除了实时的人机识别,根据还可以根据一些账号或者ip平时的购物等行为进行用户画像识别出其中的黄牛,机器账号等等,维持着一个列表,对于列表中的账号可以按风险等级进行额外的拦截。

下图一个接入用户行为识别前后的一个流量对比图

可以明显的看到,两天的同一时刻,在未接入识别时流量峰值为60w ,接入识别后流量降为30w 。也就意味着有人通过脚本等工具贡献了超过一半的请求量;另一个比对是,在没有接入识别时,我们一个活动数万奖品,在活动开始3秒钟就已经被抽光,而接入之后,当活动结束时刚好被抽完。所以,如果没有行为识别的拦截,不少正常用户根本抽不到奖品,这点跟春节抢火车票是一样的场景。

c).其他规则

其他规则包括缓存中的活动限制规则等等,根据一些简单的逻辑,也起到一定作用的流量削峰。

至此,我们所有的流量削峰思路都已经解释完了,接下来是针对性能优化做的一些工作。

2.3应用层的性能优化

性能优化是一个庞大的话题,从代码逻辑,缓存,到数据库索引,从负载均衡到读写分离,能谈的事情太多了。在我们的这个高并发系统中,性能的瓶颈在于数据库的压力,这里就聊下我们的一些解决思路。

a).缓存

缓存是降低数据库压力的有效手段,我们使用到的缓存分为两块。

  1. 分布式缓存:Ycache是1号店基于MemCache二次开发的一个分布式缓存组件,我们将跟用户相关的,数据规模大的数据缓存在Ycache中,减少不必要的读写操作。

  2. 本地缓存:使用分布式缓存降低数据库压力,但仍然有一定的网络开销,对于数据量小,无需更新的一些热数据,比如活动规则,我们可以直接在web服务器本地缓存。代表性的是EhCache了,而我们那时比较直接粗暴,直接用ConcurrentHashMap造了个轮子,也能起到同样的效果。

b).无事务

对于并发的分布式系统来说,数据的一致性是一个必须考虑的问题。在我们抽奖系统中,数据更需要保证一致,活动奖品是1台iphone,就绝不能被抽走两台。常见的做法便是通过事务来控制,但考虑到我们业务逻辑中的如下场景。

在JDBC的事务中,事务管理器在事务周期内会独占一个connection,直到事务结束。

假设我们的一个方法执行100ms,前后各有25ms读写操作,中间向其他SOA服务器做了一次RPC,耗时50ms,这就意味着中间50ms时connection将处于挂起状态。

前面已经谈到了当前性能的瓶颈在于数据库,因此这种大事务等于将数据库链接浪费一半,所以我们没有使用事务,而是通过以下两种方式保证数据的一致性。

  1. 乐观锁:在update时使用版本号的方式保证数据唯一性,比如在用户中奖后减少已有奖品数量,

    update award set award_num=award_num-1,version=version+1 where id=#{id} and version=#{version} and award_num>0
    
  2. 唯一索引:在insert时通过唯一索引保证只插入一条数据,比如建立奖品id和用户id的唯一索引,防止insert时插入多条中奖记录。

2.4 数据库及硬件

再往下就是基础层了,包括我们的数据库和更底层的硬件,之所以单独列一节,是为了聊聊我们踩的一个坑。

当时为了应对高并发的场景,我们花了数周重构,从前台服务器到后台业务逻辑用上了各种优化手段,自认为扛住每分钟几十万流量不成问题,但这都是纸上谈兵,我们需要拿数据证明,因此用JMeter做了压测。

首先是流量预估,我们统计了过往的数据,预估的流量是15w/分钟,单次请求性能指标是100ms左右,因此吞吐量为150000/60~2500tps,每次请求100ms,即并发数为250,这只是平均的,考虑活动往往最开始几秒并发量最大,所以峰值并发估计为平均值的3-5倍。

第一次我们用50个并发做压测:

压测结果简直难以置信,平均耗时超600ms,峰值轻松破1000ms,这连生产上日常流量都扛不住,我们做了这么多手段,不应该性能反而降低了,当时都有点怀疑人生了,所以我们着手开始排查原因。

首先查看日志发现数据库链接存在超时

排查发现配置的数据库链接数为30,50个线程并发情景下会不够,将最大链接数设为100.数据库链接超时问题没有了,但问题没这么简单,测试下来还是一样的结果。

然后通过VisualVM连上压测的JVM,我们查看了线程的快照。

(点击放大图像)

如图,发现在几个数据库写方法以及一个RPC接口上的耗时占比最大。

所以一方面我们自己着手查原因,另一方面也推动接口提供方减少耗时。

首先是一些常规的排查手段

  1. 走读对应部分代码,排查是否有锁,或者严重的逻辑错误如死循环等。
  2. dump虚拟机内存快照,排查是否存在死锁。
  3. 查看sql语句及其执行计划,确保业务逻辑合理,并走到索引。
  4. ...

当时花了两天时间毫无进展,代码上没发现任何问题,也请教了很多同事,感觉已经陷入了思维误区,然后有位同事说这不是我们程序的问题,会不会是数据库本身或者硬件问题。我们马上找了DBA的同事,查看测试数据库的执行情况,如图:

log file sync的Avg wait超过了60ms,查阅资料后了解到这种情况的原因可能有:

  1. 连接阻塞;
  2. 磁盘io瓶颈;

然后我们一看,压测环境的服务器的硬盘是一块老的机械硬盘,而其他环境早已SSD遍地了。我们连夜把压测环境切换到了SSD,问题解决了,最后压测结果:单机441个并发, 平均响应时间136ms,理论上能扛住19w/分钟的流量,比起第一次压测有了数十倍的提升,单机即可扛住预估流量的压力,生产上更不成问题了,可以上线了。

至此,整个抽奖系统的架构,以及我们限流削峰和调优的所有手段已经介绍完了,接下来展开下其他的优化想法和感悟吧。

3.其他优化想法

这里还有一些曾经考虑过的想法供参考,可能由于时间,不适用等原因没有做,但也是应对高并发场景的思路。

  1. 消息队列:由于抽奖一般会有个转盘效果,意味着我们不需要马上给出结果,如果引入消息队列,无疑可以有效削峰,降低服务器压力。如果说Tomcat的并发配置和信号量的硬削峰是把1000并发直接拒掉500来做到,而这种是把1000并发排队每次处理500来实现,也就是说结果上是会处理掉所有请求,相对来说更合理。1号店的秒杀系统便接入了这个功能,但由于当时重构时间只有两周,评估下来时间上来不及做,因此搁置了。

  2. 异步:前面谈到了一个RPC接口占用了近50%的耗时,经过业务逻辑上的评估这个接口是可以异步的,所以如果有必要的时候这是一个可行的方案。

  3. 读写分离:主备库的同步还是有延迟的,基于一致性考虑,读写分离的方案被我们抛弃了,但在其他高并发场景,读写分离是一个比较常见的优化方案。

  4. 活动拆库:性能的瓶颈还是在数据库,如果多个活动并行,并且互不相干,我们完全可以按活动拆库,分担数据库压力,不过这次的压力还没有达到这个量。

  5. 内存数据库:数据库的IO效率影响很大,把数据库所在的机械硬盘换成SSD后有数倍性能的提升,但内存的速度更快,相关文章已经介绍到12306已经全面应用了。

  6. 升级硬件:换了SSD后性能就上来了,在未来如果有了瓶颈,可以预见的是如果硬件的有了新的发展,通过升级硬件是比较省力的方式。

4.几点思考

  1. 警惕流量,用户量的增长:在没有引入行为识别前,看着监控里流量十万十万的上涨无疑是很高兴的,但引入用户行为识别后,我们发现一大半的流量可能来自于脚本。假设我们没有做行为识别,一个普通用户,稍微慢几秒就得不到奖品,来这么两三次,估计就不会来参加你的活动了,正常用户就这么一个个流失了,这种负面影响想想就让人背后发凉。所以当看到用户量快速增长,在高兴的同时,一定要意识到其中可能的风险,引入必要风控手段,保证真正的用户的用户体验。

  2. 性能优化是系统性的问题:从前台到后台我们考虑了很多优化方式,但最后压测不通过,一头栽在了老化的硬盘上,真是一个活生生的短板理论例子,所以优化不能单单局限代码,JVM的层次,从页面到硬盘,一定要通盘考虑。在遇到性能瓶颈时,不要只从表面的代码排查问题,要深入,网络,硬件都有可能瓶颈。

关于作者

文初开,开发工程师,15年加入1号店,现负责1号店购物流程的开发工作,曾负责1号店会员、抽奖系统的开发工作,博客:https://chulung.com

转载于:https://www.cnblogs.com/davidwang456/articles/8064229.html

从限流削峰到性能优化,谈1号店抽奖系统架构实践相关推荐

  1. 限流削峰——限流器的实现

    什么是限流削峰 限流削峰是对服务端进行流量控制的常见手段,控制QPS上限以达到减轻服务端负担的目的.一个好的限流削峰方案应该实现以下三条原则: 对系统入侵性小,与业务充分解耦,以降低维护成本 使用合适 ...

  2. 分布式技术与实战第七课 高并发下高可用的熔断、降级、限流和负载均衡、监控以及统一的日志系统

    第39讲:从双十一看高可用的保障方式 从这一课时开始,专栏内容进入最后一个模块,即分布式高可用系列,这部分的内容,我将以电商大促为背景,讲解系统限流.降级熔断.负载均衡.稳定性指标.系统监控和日志系统 ...

  3. sql2005性能优化(在32位系统上突破2G内存使用量的方法)

    服务器磁盘为(SAS)IBM组成RAID0+1,SQL2K5只识别4G内存,实际只占用2G内存.而使用 AWE的话,应用程序可以直接将操作系统允许的最大物理内存量保留为未分页的内存.使用 AWE 使  ...

  4. 【转】关于WaterFall瀑布流式布局的性能优化

    市面上已存在的瀑布流式布局的网站: 拼范网:http://www.pinfun.com/ 迷尚网:http://www.mishang.com/ 凡客达人:http://star.vancl.com/ ...

  5. 大流量限流/消峰案例

    1.分布式系统为什么需要进行流量管制 地铁高峰期流量管制 五种分布式系统应对高并发.大流量的常规手段: 1.扩容 2.动静分离 3.缓存 4.服务降级 5.限流 2.限流的具体方案 2.1.常见的限流 ...

  6. 架构升级、性能优化,高德技术专家 infoQ 全球架构师峰会开讲啦

    关于全球架构师峰会(ArchSummit) 全球架构师峰会(ArchSummit)是 InfoQ 中国团队推出的重点面向高端技术管理者.架构师的技术会议,54% 参会者拥有8年以上工作经验.7月12日 ...

  7. 吐血整理的 Android 性能优化思维导图,让面试官眼前一亮

    引言 现如今 Android 开发行业的主要问题是因为初级的 Android 开发者太多了,导致初级开发的市场过于饱和,所以也就进一步导致初级和中级的开发者面临更大的竞争,因此想要脱离这种竞争现状,只 ...

  8. 面试限流、熔断、高可用,好多人一脸懵!

    欢迎关注方志朋的博客,回复"666"获面试宝典 日常生活中,有哪些需要限流的地方? 像我旁边有一个国家景区,平时可能根本没什么人前往,但是一到五一或者春节就人满为患,这时候景区管理 ...

  9. 软件教练说:性能优化与性能设计,“相亲相爱”的一对

    摘要:性能优化通常是在现有系统和代码基础上做改进,考验的是开发者反向修复的能力,而性能设计考验的是设计者的正向设计能力,但性能优化的方法可以指导性能设计,两者互补. 性能优化是指在不影响正确性的前提下 ...

最新文章

  1. 按照文字内容动态设置TableViewCell的高度
  2. 动态路由器与静态路由器的理论知识
  3. 网络营销外包立足用户角度完成企业网站网络营销外包优化
  4. 我记录网站综合系统 -- 技术原理解析[3:我记录框架处理流程]
  5. 线程间通信及虚假唤醒
  6. 15行代码AC——习题5-5 复合词(Compound Words, UVa 10391)——解题报告
  7. 机器人最新天赋符文天赋加点图_常德2020中小学机器人竞赛开赛 286名选手现场比拼技能...
  8. SQL Server 2014如何提升非在线的在线操作
  9. 五一四天假公布后携程机票大涨价 官方如此回应
  10. java 清单文件 生成,使用批处理文件生成文件列表清单
  11. 南瑞科技服务器型号,南瑞--NSC通讯概述
  12. windows下一键修改IP地址
  13. 人事、财务常用EXCEL基础函数应用示例总结
  14. 普林斯顿微积分读本篇十二:洛必达法则
  15. 仗剑走天涯,执手闯天下
  16. 【SQL注入技巧拓展】————14、Bypass 360主机卫士SQL注入防御(附tamper脚本)
  17. Snaker-flow介绍
  18. javaweb实现购物车功能
  19. python 游戏按键精灵 PyDirectInput介绍
  20. 解决树莓派4B无线鼠标迟滞/延迟的问题

热门文章

  1. 使用netfilter框架处理ARP报文
  2. 快速mysql导入sql文件_mysql肿么快速从sql文件导入数据库
  3. 调用ajax_[WEB篇]-AJAX-02-AJAX应用案例
  4. PercentFrameLayout(百分比布局)的基本使用
  5. 深度linux腾讯视频,在UOS/Deepin 20/Ubuntu 18.04下安装腾讯视频Linux版的方法
  6. opencv下载安装及介绍【初学,后续继续更新】
  7. 画出沪深300指数曲线
  8. 递归 算例一(求一个简单嵌套字典的深度)
  9. 整合rpc远程调用_远程过程调用(RPC)
  10. Mac~git学习和应用需要注意的几个点