电商项目_秒杀_架构及核心

发布于:2025-07-23 ⋅ 阅读:(16) ⋅ 点赞:(0)

秒杀架构设计

 先看下普通web项目架构: 

(Nginx : 反向代理、负载均衡,一般是运维部分做生产搭建的时候配置好)

秒杀架构设计:

和普通架构区别:

  • 原先由Web 服务或Nginx服务提供的静态资源放到了CDN
  • Nginx的职责放⼤,前置⽤来做Web ⽹关,承担部分业务逻辑校验,并且可能增加⿊⽩名单、限流和流控的功能

秒杀技术选型

核⼼设计是对巨⼤的瞬间流量进⾏层层错峰

错峰1:页面静态化。将包含浏览者信息的动态数据和不包含的静态数据进行区分。

错峰2:秒杀前答题。⽬的是防⽌机器刷单,以及错开⽤户的下单时⻓。

错峰3:Redis扣减库存。快速扣减库存, 扣减库存完通知nginx.

错峰4:Nginx快速通知秒杀结束。

错峰5:MQ进行流量消峰

错峰6:引⼊ MQ 进⾏下单服务异步化

秒杀后端链路图

—场完整的秒杀活动的⼤概流程是这样的:

1.运营⼈员在秒杀系统的运营后台,根据指定商品,创建秒杀活动,指定活动的开始时间、结束时间、活动库存等。

2.活动开始之前,由秒杀系统运营后台开启秒杀,会同时往商城系统的Redis Cluster集群写⼊ ⾸⻚秒杀活动信息 和往秒杀系统的Redis主从集群写诸如 秒杀商品库存 等信息。

3.⽤户进⼊到秒杀商详⻚准备秒杀。

4.商品详情⻚可以看到⽴即抢购的按钮,这⾥我们可以通过增加⼀些逻辑判断来限制按钮是否可以点击,⽐如是否设置了抢购⽤户等级限制,是否还有活动库存,是否设置了预约等等。如果都没限制,⽤户可以点击抢购按钮,进⼊到秒杀结算⻚。

5.在结算⻚,⽤户可更改购买数量,切换地址、⽀付⽅式等,这⾥的结算元素也需要按实际业务来定,更复杂的场景还可以⽀持积分、优惠券、红包、配送时效等,并且这些都会影响最终价格的计算。

6.确认⽆误后,⽤户提交订单,在这⾥后端服务可以调⽤⻛控、限购等接⼝,来完善校验,都通过之后,完成库存的扣减和订单的⽣成。

7.订单完成后,根据⽤户选择的⽀付⽅式跳转到对应的⻚⾯,⽐如在线⽀付就跳转到收银台,货到付款的话,就跳到下单成功提示⻚。

相关技术点:

  • 秒杀活动开始:Canal组件监听DB里秒杀活动变化,同步到Redis,并在首页展示
  • 商品详情页:Nginx配置了详情页的页面,动态数据从Redis中获取, 静态数据在Nginx中配置好
  • 确认订单:
  • 生成订单:Redis中扣减库存

秒杀前流量管控

秒杀中的流量管控

1. 流量削峰

  1. 验证码和回答问题:有两个⽬的,⼀是快速拦截掉部分刷⼦流量,防⽌机器作弊,起到防刷的作⽤;⼆是平滑秒杀的⽑刺请求,延缓并发,对流量进⾏削峰。
  2. 消息调用:用到两个业务系统之间的调用中

2. 限流

Nginx限流

Nginx本身也提供了⾮常强⼤的限流功能,⽐如有两个专⻔的限流模块HttpLimitzone和HttpLimitReqest

  1. HttpLimitzone⽤来限制⼀个客户端的并发连接数,
  2. HttpLimitReqest通过漏桶算法来限制⽤户的连接频率
// HttpLimitzone示例
// 表示同⼀ip不同请求地址,进⼊名为one的zone,限制速率为1请求/秒。

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    server {
        location /search/ {
        limit_req zone=one burst=2 nodelay;
        }
    }
}

应用/服务层限流

1. 线程池:限制连接数

2. 限流组件:QPS的限流

3. 自定义限流:

        比如用户的下订单请求,前端在用户进入订单结算页面时,调用订单号服务生成个orderId,在用户提交订单时,带着这个orderId. 

        订单号的获取,秒杀场景下的改进是:JVM缓存放置预先生成的orderId, 定时任务定时去生成新的orderId并放入缓存中(注意保证线程安全)。

        JVM缓存中的订单号起到限流的作用, 同时前端提前生成orderId, 也防止了同一个订单提交多次。

分层过滤

举个例子,秒杀如果商品售罄,会将库存为0的结果写到nginx缓存

1. nginx判断库存, 先判断本地缓存, 在判断redis缓存, 结果为0时终止秒杀

2. 业务链路中,同样都会做库存的判断

业务上还要解决退单导致库存量增加,思路:订阅对应的Redis的channel.

3. 限购

限购的策略:

  • 商品维度:秒杀产品的总量、商品发货地 等等
  • 个人维度:1个手机号1单、1人1天1单 等等

秒杀活动时, 流量先经过限流策略过滤后,在进入到订单服务。通过限购策略,也可以过滤到一部分流向订单系统的流量。

库存扣减

库存扣减涉及到两个核心操作,查商品库存,库存充足扣减库存。要保证其原子性

1. 数据库

1. 查询和扣减放在一个事务中

2. where 条件保证库存 >= 0

3. 乐观锁, 先查询,更新时带着版本号

4. 数据库特性:无符号整数字段,值不能小于0, 所以扣减到小于0,SQL报错(性能超,不适合秒杀,适合业务量小的场景)

2. 分布式锁

商品维度加分布式锁,但不适合秒杀场景。

  • 过期时间的设置,太短,业务流程未走完就过期了;太长,如果当前业务流程异常,导致其他线程一直阻塞锁,性能不高。
  • 秒杀场景,商品还串行下单,性能肯定不好

3. 高并发扣减

降级

降级⼀般是有损的,那么必然要有所牺牲,⼏种常⻅的降级:写服务降级、读服务降级。

1. 写服务降级:牺牲数据─致性获取更⾼的性能。 降级手段,同步写数据库降级成同步写缓存、异步写数据库

Redis扣减库存,通过lua脚本,保证查询和扣减原子性的执行

2. 读服务降级:故障场景下紧急降级快速⽌损。

        在做⾼可⽤系统设计时,要牢记就是微服务⾃身所依赖的外部中间件服务或者其他RPC服务,随时都可能发⽣故障,因此我们需要建设多级缓存,以便故障时能及时降级⽌损。

        假设当秒杀的Redis缓存出现故障时,我们就可以通过降级开关,快速将读请求降级到 从Redis 缓存、MongoDB或者ES上。或者当Redis和备份缓存同时出现故障时(现实中很少出现同时故障的场景),我们还是可以通过降级开关将流量切换到数据库上,让数据库暂时承压来完成读请求服务。

简化系统设计

        简化系统功能就是指⼲掉⼀些不必要的流程,舍弃⾮核⼼功能。

        商品的基本信息外,还有很多附加的信息,⽐如你是否收藏过该商品、商品的收藏总数量、商品的排⾏榜、评价和推荐等楼层。同样,对于秒杀结算⻚,还会有礼品卡、优惠券等虚拟⽀付路径。

        不过,实际运⽤中,这种⾮核⼼功能的有损降级,要视具体的SKU⽽定,⼀般为了降低影响范围,我们只对流量⾮常⾼的SKU进⾏降级。⽐如,如果是⼿机秒杀,⼀般是不需要降级的,但是像⼝罩这样的爆品,就需要针对SKU维度进⾏⾮核⼼功能的降级了。

        降级开关的怎么设计呢,其实⽐较简单,核⼼思路就是通过配置中⼼,对降级开关进⾏变更,然后推送到各个微服务实例上。

4. 热点数据

        ⼀般⾼并发的常规解决思路是:数据库通过分库分表来应对;Redis,增加Redis集群的分⽚来解决,⽽应⽤层⼀般是⽆状态的设计。所以从数据库、Redis缓存到应⽤服务,都是可以通过增加机器来⽔平扩展服务能⼒,解决⾼并发的问题。

        但是秒杀场景,就那么几个商品,这些手段显然不够。

1. 读热点

1. 增加热点数据的副本数。 增加Redis从的副本数,然后业务层(Tomcat集群)轮询查询不同的副本,提⾼同⼀数据的QPS。

2. 让热点数据离⽤户越近越好。

  • 把热点数据再上移, 在服务内部做热点数据的本地缓存。但是本地缓存的数据延迟要有解决方案
  • 如果还是用redis缓存,nginx和nginx需要的缓存部署在一起,业务服务和业务服务使用的缓存部署在一起, 做到不跨网络访问。

3. 直接短路访问。 要具体业务具体分析, 根据商品的配置,直接不走一些业务逻辑。

2. 写热点

  1. 预约人数场景, 实际展示的预约人数并不要求十分精确, 对应的解决方式:先在JVM内存⾥累加,延迟提交到 Redis,这样就可以把 Redis 的OPS降低⼏⼗倍。
  2. 写对象颗粒化,库存的扣减场景,可以通过把⼀个热 key拆解成多个key的⽅式,避免热点问题。
  3. 单SKU的库存直接在Redis 单分⽚上进⾏扣减。实际上,扣减库存在秒杀链路的末端,通过之前的削峰和限流的各种⼿段,真正到库存的流量是有限的,单⽚的Redis OPS 能承受得了。然后,我们可以针对单SKU的库存扣减进⾏单独限流,保证库存单⽚Redis的压⼒。这样双管⻬下,单SKU的库存Redis 扣减压⼒就是可控的了。

相关技术

OpenResty

        Nginx在主要的秒杀系统设计中,扮演着⾮常重要的⻆⾊,意味着Nginx上要承载很多的业务逻辑。Nginx的底层模块⼀般都是⽤C语⾔写的,如果我们想在Nginx的基础之上写业务逻辑会很不⽅便,所以这个时候我们还得借助OpenResty,它是Nginx的⼀个社区分⽀。

        按照官⽹的说法,OpenResty 是⼀个基于 Nginx 与 Lua 的⾼性能 Web 平台,其内部集成了⼤量精良的 Lua库、第三⽅模块以及⼤多数的依赖项。⽤于⽅便地搭建能够处理超⾼并发、扩展性极⾼的动态 Web 应⽤、Web 服务和动态⽹关。

        OpenResty本质上是将 LuaJIT 的虚拟机嵌⼊到 Nginx 的管理进程和⼯作进程中,同⼀个进程内的所有线程都会共享这个虚拟机,并在虚拟机中执⾏Lua代码。在性能上,OpenResty接近或超过 Nginx 的C模块,⽽且开发效率更⾼。


网站公告

今日签到

点亮在社区的每一天
去签到