Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战

Java生鲜电商平台-  什么是秒杀

通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动

比如说京东秒杀,就是一种定时定量秒杀,在规定的时间内,无论商品是否秒杀完毕,该场次的秒杀活动都会结束。这种秒杀,对时间不是特别严格,只要下手快点,秒中的概率还是比较大的。

淘宝以前就做过一元抢购,一般都是限量 1 件商品,同时价格低到「令人发齿」,这种秒杀一般都在开始时间 1 到 3 秒内就已经抢光了,参与这个秒杀一般都是看运气的,不必太强求

业务特点

 

 

瞬时并发量大

秒杀时会有大量用户在同一时间进行抢购,瞬时并发访问量突增 10 倍,甚至 100 倍以上都有。

库存量少

一般秒杀活动商品量很少,这就导致了只有极少量用户能成功购买到。

业务简单

流程比较简单,一般都是下订单、扣库存、支付订单

技术难点

 

现有业务的冲击

秒杀是营销活动中的一种,如果和其他营销活动应用部署在同一服务器上,肯定会对现有其他活动造成冲击,极端情况下可能导致整个电商系统服务宕机

直接下订单

下单页面是一个正常的 URL 地址,需要控制在秒杀开始前,不能下订单,只能浏览对应活动商品的信息。简单来说,需要 Disable 订单按钮

页面流量突增

秒杀活动开始前后,会有很多用户请求对应商品页面,会造成后台服务器的流量突增,同时对应的网络带宽增加,需要控制商品页面的流量不会对后台服务器、DB、Redis 等组件的造成过大的压力

架构设计思想

 

限流

由于活动库存量一般都是很少,对应的只有少部分用户才能秒杀成功。所以我们需要限制大部分用户流量,只准少量用户流量进入后端服务器

削峰

秒杀开始的那一瞬间,会有大量用户冲击进来,所以在开始时候会有一个瞬间流量峰值。如何把瞬间的流量峰值变得更平缓,是能否成功设计好秒杀系统的关键因素。实现流量削峰填谷,一般的采用缓存和 MQ 中间件来解决

异步

秒杀其实可以当做高并发系统来处理,在这个时候,可以考虑从业务上做兼容,将同步的业务,设计成异步处理的任务,提高网站的整体可用性

缓存

秒杀系统的瓶颈主要体现在下订单、扣减库存流程中。在这些流程中主要用到 OLTP 的数据库,类似 MySQL、SQLServer、Oracle。由于数据库底层采用 B+ 树的储存结构,对应我们随机写入与读取的效率,相对较低。如果我们把部分业务逻辑迁移到内存的缓存或者 Redis 中,会极大的提高并发效率

整体架构

 

 

客户端优化

秒杀页面

秒杀活动开始前,其实就有很多用户访问该页面了。如果这个页面的一些资源,比如 CSS、JS、图片、商品详情等,都访问后端服务器,甚至 DB 的话,服务肯定会出现不可用的情况。所以一般我们会把这个页面整体进行静态化,并将页面静态化之后的页面分发到 CDN 边缘节点上,起到压力分散的作用

防止提前下单

防止提前下单主要是在静态化页面中加入一个 JS 文件引用,该 JS 文件包含活动是否开始的标记以及开始时的动态下单页面的 URL 参数。同时,这个 JS 文件是不会被 CDN 系统缓存的,会一直请求后端服务的,所以这个 JS 文件一定要很小。当活动快开始的时候(比如提前),通过后台接口修改这个 JS 文件使之生效

API 接入层优化

客户端优化,对于不是搞计算机方面的用户还是可以防止住的。但是稍有一定网络基础的用户就起不到作用了,因此服务端也需要加些对应控制,不能信任客户端的任何操作。一般控制分为 2 大类

限制用户维度访问频率

针对同一个用户( Userid 维度),做页面级别缓存,单元时间内的请求,统一走缓存,返回同一个页面

限制商品维度访问频率

大量请求同时间段查询同一个商品时,可以做页面级别缓存,不管下回是谁来访问,只要是这个页面就直接返回

SOA 服务层优化

上面两层只能限制异常用户访问,如果秒杀活动运营的比较好,很多用户都参加了,就会造成系统压力过大甚至宕机,因此需要后端流量控制

对于后端系统的控制可以通过消息队列、异步处理、提高并发等方式解决。对于超过系统水位线的请求,直接采取 「Fail-Fast」原则,拒绝掉

秒杀整体流程图

 
 

秒杀系统核心在于层层过滤,逐渐递减瞬时访问压力,减少最终对数据库的冲击。通过上面流程图就会发现压力最大的地方在哪里?

MQ 排队服务,只要 MQ 排队服务顶住,后面下订单与扣减库存的压力都是自己能控制的,根据数据库的压力,可以定制化创建订单消费者的数量,避免出现消费者数据量过多,导致数据库压力过大或者直接宕机。

库存服务专门为秒杀的商品提供库存管理,实现提前锁定库存,避免超卖的现象。同时,通过超时处理任务发现已抢到商品,但未付款的订单,并在规定付款时间后,处理这些订单,将恢复订单商品对应的库存量

Nginx优化

  1. 动静分离,不走tomcat获取静态资源
 server {
listen 8088;
location ~ \.(gif|jpg|jpeg|png|bmp|swf)$ {
root C:/Users/502764158/Desktop/test;
} location ~ \.(jsp|do)$ {
proxy_pass http://localhost:8082;
}
}
}
  1. gzip压缩,减少静态文件传输的体积,节省带宽,提高渲染速度
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_comp_level 3;
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain application/x-javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
  1. 配置集群负载和容灾,设置失效重连的时间,失效后,定期不会再重试挂掉的节点,参数
  • fail_timeout默认为10s
  • max_fails默认为1。就是说,只要某个server失效一次,则在接下来的10s内,就不会分发请求到该server上
  • proxy_connect_timeout 后端服务器连接的超时时间_发起握手等候响应超时时间
upstream  diancai.com {
#服务器集群名字
server 127.0.0.1:8080;
server 127.0.0.1:38083;
server 127.0.0.1:8083;
} server {
listen 88;
server_name localhost;
location / {
proxy_pass http://diancai.com;
proxy_connect_timeout 1;
fail_timeout 5;
}
}
  1. 集成Varnish做静态资源的缓存
  2. 集成tengine做过载的保护

页面优化

  1. 降低交互的压力
  • 尽量把js、css文件放在少数几个里面,减少浏览器和后端交互获取静态资源的次数
  • 尽量避免在秒杀商品页面使用大的图片,或者使用过多的图片
  1. 安全控制
  • 时间有效性验证:未到秒杀时间不能进行抢单,并且同时程序后端也要做时间有效性验证,因为网页的时间和各自的系统时间决定,而且秒杀器可以通过绕开校验直接调用抢单
  • 异步抢单:通过点击按钮刷新抢宝,而不是刷新页面的方式抢宝(答题验证码等等也是ajax交互)
  • redis做IP限流
  • redis做UserId限流

Redis集群

  1. 分布式锁(悲观锁)

  2. 缓存热点数据(库存):如果QPS太高的话,另一种方案是通过localcache,分布式状态一致性通过数据库来控制

  3. 分布式悲观锁(参考redis悲观锁的代码)

  • 悲观锁(因为肯定争抢严重)
  • Expire时间(抢到锁后,立刻设置过期时间,防止某个线程的异常停摆,导致整个业务的停摆)
  • 定时循环和快速反馈(for缓存有超时设置,每次超时后,重新读取一次库存,还有货再进行第二轮的for循环争夺,实现快速反馈,避免没有货了还在持续抢锁)
  1. 异步处理订单
  • redis抢锁成功后,记录抢到锁的用户信息后,就可以直接释放锁,并反馈用户,通过异步的方式来处理订单,提升秒杀的效率,降低无意义的线程等待
  • 为了避免异步的数据不同步,需要抢到锁的时候,在redis里面缓存用户信息列表,缓存结束后,触发抢单成功用户信息持久化,并且定时的比对一致性

消息队列限流

消息队列削峰限流(RocketMQ自带的Consumer自带线程池和限流措施),集群。一般都是微服务,订单中心、库存中心、积分中心、用户的商品中心

数据库

  • 拆分事务提高并发度
  • 根据业务需求考虑分库:读写分离、热点隔离拆分,但是会引入分布式事务问题,以及跨库操作的难度
    要执行的操作:扣减库存、生成新订单、生成待支付订单、扣减优惠券、积分变动
    库存表是数据库并发的瓶颈所在,需要在事务控制上做权衡:可以把扣减库存设置成一个独立的事务,其它操作成一个大的事务(订单、优惠券、积分操作),提高并发度,但是要做好额外的check
    update 库存表 set 库存=库存-1 where id=** and 库存>1
  • 为了提升并发,需要在事务上做妥协
    单机上拆分事务:比如扣减库存表+(生成待支付订单+优惠券扣减+积分变动)是一个大的事务,为了提高并发,可以拆分为2个事务
    分库以后引入分布式事务问题,为了保证用户体验,最好还是通过日志分析来人工维护,否则阻塞太严重,并发差

答题验证码

  1. 可以防止秒杀器的干扰,让更多用户有机会抢到
  2. 延缓请求,每个人的反应时间不同,把瞬间流量分散开来了
  3. 验证码的设计可以分为2种
  • 验证失败重新刷新答题(12306):服务器交互量大,每错一次交互一次,但是可以大大降低秒杀器答题的可能性,因为没有试错这个功能,答题一直在变
    验证失败提示失败,但是不刷新答题的算法:要么答题成功,进入下单界面,要么提示打错,继续答题(不刷新答题,无须交互,用js验证结果)。
    这种方案,可以在加载题目的时候一起加载MD5加密的答案,然后后台再校验一遍,实现类似的防止作弊的效果。好处是不需要额外的服务器交互。
    MD加密答案的算法里面要引入 userId PK这些因素进来来确保每次答案都不一样而且没有规律,避免秒杀器统计结果集

  • 答题的验证:除了验证答案的正确性意外,还要统计反应时间,例如12306的难题,正常人类的答题速度最快是1.5s,那么,小于1s的验证可以判定为机器验证

总结

层层过滤,尽量将请求拦截在上游,降低下游的压力,充分利用缓存与消息队列,提高请求处理速度以及削峰填谷的作用

削峰限流

  • 前端+Redis拦截,只有redis扣减成功的请求才能进入到下游
  • MQ堆积订单,保护订单处理层的负载,Consumer根据自己的消费能力来取Task,实际上下游的压力就可控了。重点做好路由层和MQ的安全
  • 引入答题验证码、请求的随机休眠等措施,削峰填谷

安全保护

  • 页面和前端要做判断,防止活动未开始就抢单,防止重复点击按钮连续抢单
  • 防止秒杀器恶意抢单,IP限流、UserId限流限购、引入答题干扰答题器,并且对答题器答题时间做常理推断
  • 过载丢弃,QPS或者CPU等核心指标超过一定限额时,丢弃请求,避免服务器挂掉,保证大部分用户可用

页面优化,动静分离

  • 秒杀商品的网页内容尽可能做的简单:图片小、js css 体积小数量少,内容尽可能的做到动静分离
  • 秒杀的抢宝过程中做成异步刷新抢宝,而不需要用户刷新页面来抢,降低服务器交互的压力
  • 可以使用Nginx的动静分离,不通过传统web浏览器获取静态资源
  • nginx开启gzip压缩,压缩静态资源,减少传输带宽,提升传输速度
  • 或者使用Varnish,把静态资源缓存到内存当中,避免静态资源的获取给服务器造成的压力

异步处理

  • redis抢单成功后,把后续的业务丢到线程池中异步的处理,提高抢单的响应速度
  • 线程池处理时,把任务丢到MQ中,异步的等待各个子系统处理(订单系统、库存系统、支付系统、优惠券系统),异步操作有事务问题,本地事务和分布式事务,但是为了提升并发度,最好牺牲一致性。通过定时扫描统计日志,来发现有问题的订单,并且及时处理

热点分离

尽量的避免秒杀功能给正常功能带来的影响,比如秒杀把服务器某个功能拖垮了
分离可以提升系统的容灾性,但是完全的隔离的改造成本太高了,尽量借助中间件的配置,来实现冷热分离

  • 集群节点的分离:nginx配置让秒杀业务走的集群节点和普通业务走的集群不一样。
  • MQ的分离:避免秒杀业务把消息队列堆满了,普通业务的交易延迟也特别厉害。
  • 数据库的分离:根据实际的秒杀的QPS来选择,热点数据分库以后,增加了分布式事务的问题,以及查询的时候跨库查询性能要差一些(ShardingJDBC有这种功能),所以要权衡以后再决定是否需要分库

避免单点

各个环节都要尽力避免

降级

临时关闭一些没那么重要的功能,比如秒杀商品的转赠功能、红包的提现功能,待秒杀峰值过了,设置开关,再动态开放这些次要的功能

Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战的更多相关文章

  1. Java生鲜电商平台-高可用微服务系统如何设计?

    Java生鲜电商平台-高可用微服务系统如何设计? 说明:Java生鲜电商平台高可用架构往往有以下的要求: 高可用.这类的系统往往需要保持一定的 SLA,7*24 时不间断运行不代表完全不挂,而是有一定 ...

  2. Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构

    Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构 概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留 ...

  3. Java生鲜电商平台-如何使用微服务来架构生鲜电商B2B2C平台?

    Java生鲜电商平台-如何使用微服务来架构生鲜电商B2B2C平台? 说明:随着互联网的日益普及,人们通过手机下单买菜的人越来越多,生鲜这个行业有两个显著的特点,一个是刚需.(你每天都要吃饭,都要吃菜) ...

  4. Java生鲜电商平台-Spring Cloud微服务架构图

  5. Java生鲜电商平台-商城系统库存问题分析以及产品设计对逻辑/物理删除思考

    Java生鲜电商平台-商城系统库存问题分析以及产品设计对逻辑/物理删除思考 说明:在生鲜电商的库存设计,是后台的重点,也是难点,关乎商品是否存在超卖.商品的库存增加方式倒不难,直接在后台添加即可,而扣 ...

  6. Java生鲜电商平台-优惠券系统的架构设计与源码解析

    Java生鲜电商平台-优惠券系统的架构设计与源码解析 电商后台:实例解读促销系统 电商后台系统包括商品管理系统.采购系统.仓储系统.订单系统.促销系统.维权系统.财务系统.会员系统.权限系统等,各系统 ...

  7. Java生鲜电商平台-App系统架构开发与设计

    Java生鲜电商平台-App系统架构开发与设计 说明:阅读此文,你可以学习到以下的技术分享 1.Java生鲜电商平台-App架构设计经验谈:接口的设计2.Java生鲜电商平台-App架构设计经验谈:技 ...

  8. Java生鲜电商平台-促销系统的架构设计与源码解析

    Java生鲜电商平台-促销系统的架构设计与源码解析 说明:本文重点讲解现在流行的促销方案以及源码解析,让大家对促销,纳新有一个深入的了解与学习过程. 促销系统是电商系统另外一个比较大,也是比较复杂的系 ...

  9. Java生鲜电商平台-RBAC系统权限的设计与架构

    Java生鲜电商平台-RBAC系统权限的设计与架构 说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上 ...

随机推荐

  1. 基于node.js人脸识别之人脸对比

    基于node.js人脸识别之人脸对比 Node.js简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 I/O ...

  2. ubuntu 默认python版本切换

    电脑上面有些脚本是python2的,有些是python3的,但是系统默认是python2,需要设置环境变量来进行切换. python2切换到python3: echo alias python=pyt ...

  3. Web前端基础(14):jQuery基础(一)

    1. jQuery概述 1.1 为什么要使用jQuery 在用js写代码时,会遇到一些问题: window.onload 事件有事件覆盖的问题,因此只能写一个事件. 代码容错性差. 浏览器兼容性问题. ...

  4. Customize the Application UI and Behavior 自定义应用程序UI和行为

    In XAF, the business model defines the database structure and UI appearance. Changes to your persist ...

  5. 每秒100W请求,12306秒杀业务,架构如何优化?

    如<同样是高并发,QQ/微博/12306的架构难度一样吗?>一文所述,同样是高并发场景,三类业务的架构挑战不一样: QQ类业务,用户主要读写自己的数据,访问基本带有uid属性,数据访问锁冲 ...

  6. Vue.js+vue-element搭建属于自己的后台管理模板:更深入了解Vue.js(三)

    前言 上一章我们介绍了关于Vue实例中一些基本用法,但是组件.自定义指令.Render函数这些放到了本章来介绍,原因是它们要比前面讲的要难一些,组件是Vue.js最核心的功能,学习使用组件也是必不可少 ...

  7. 解决Xcode10 Library not loaded: /usr/lib/libstdc++.6造成的crash及报错

    关键字1:dyld: Library not loaded: /usr/lib/libstdc++.6.dylib   Referenced from: 关键字2:Reason: no suitabl ...

  8. OpenCV:图像的开运算与闭运算

    导包: import numpy as np import cv2 import matplotlib.pyplot as plt def show(image): plt.imshow(image) ...

  9. adb 控制手机动作

    下载adb​​工具包,解压即可  https://download.csdn.net/download/kai402458953/10784310 各种事件说明:https://blog.csdn.n ...

  10. Spring Boot 2 读取配置文件

    开发环境:IntelliJ IDEA 2019.2.2Spring Boot版本:2.1.8 新建一个名称为demo的Spring Boot项目. 一.默认配置文件 Spring Boot会读取名称a ...