案例一:秒杀系统业务场景
秒杀系统的特点:
在活动开始之前流量平稳,当活动开始的时候流量激增。
限时、限量、限价 参与秒杀活动的商品数量有限。
活动预热,在活动开始之前宣传,对活动页进行各种配置,准备活动详情,预热活动静态文件
持续时间很短
技术特点:
- 瞬时并发了流量高
- 并发读写 读比写多,因为查询的库存流量往往大于下单的库存流量。
(在应对并发读写,这种情况下可以考虑增加验证码等一系列操作、利用流量的时间分片来缓存瞬时流量)
秒杀系统的设计原则:
- 数据量尽量少。因为消息体太大会影响传输效率。如JSON序列化都会消耗CPU使用资源。
- 请求数应尽量少。因为浏览器每发送一次请求都会消耗一定的性能。减少请求数最常用的一个方案是把多个静态文件合并成一个请求。可考虑使用nginx-http-concat插件。
- 请求路径应当尽量短。请求路径可以被理解为用户端发送到服务端响应到用户端这中间的各个节点,其中包括CDN 、负载均衡器、web容器等。这些都需要建立TCP连接。所以节点离用户越近性能越好
- 尽量使用异步化的方式.。客户端在接受到所有的请求,把这些请求都直接丢尽队列,之后系统根据自身的实际情况从队列当中获取订单请求,这样可以介乎额同步化处理不了峰值的问题
- 避免单节点 单节点是所有系统设计的大忌,因为单节点意味着系统稳定性的缺失,可能会出现不可用的情况下,这样会给企业造成不可挽回的损失。
在设计系统当中避免单节点,将系统无状态化、如果无法实现则采用冗余多个备份节点来避免单节点
动静分离的设计
动静分离是指将静态页面与动态页面解耦分离,用不同系统承载对应的流量,这样可以提升整个系统的访问性能和可维护性。
- 什么是静态数据?
- CSS、JS等资源文件
- 活动页中的HTML经他i文件
- 图片等相关资源文件
- 其他与用户信息无关的静态数据
对于静态数据我们可以进行缓存.在缓存之后访问这些静态数据的效率提高了,系统运行速度更快了。
- 什么是动态数据?
与用户相关联的数据、动态生成的数据。
技术方案:
1.清晰的分层架构 2.服务架构 3.缓存架构
- 如何实现动静分离架构?
对于静态数据,建议用户缩短路径,因为路径越短访问速度越快
对于动态数据,一般用户端需要和服务端交互才能获取,所以请求路径越长访问速度就越慢。
- 使用页面静态化实现动静分离架构。直接缓存HTTP连接、而不仅仅是缓存数据
热点数据处理
什么是热点数据?什么是静态热点数据?什么是动态热点数据?
被频繁访问的数据称为热点数据。 静态数据指一些不被更改的数据,可以提前预知的数据。静态数据指一些不可被预知的数据。
- 如何发现热点数据?
静态数据比较容易发现,可以提前预知。动态数据的发现很难,但如果能实时发现热点数据,可以促进商品的销售并且对系统起到一定的保护性。
- 使用日志收集组件如Flume Flink等
- 将实时收集到的日志清洗发送到kafka当中
- 构建一套流式计算系统,实时的从kakfa消息队列当中消费日志信息
- 流式计算系统对日志信息进行实时流式计算得到热点数据,写入缓存。
- 如何处理热点数据?
优化、限制、隔离。直接将热点数据写入分布式缓存。使用线程池隔离技术将每个热点数据的处理过程放在独立的线程池当中这样可以避免大流量冲击影响其它机器。 隔离是因为不能因为数据量访问过大而造成整个业务的崩溃。
大流量的高效管控
在秒杀系统当中,流量会带来瞬时的峰值。瞬时的流量冲击可能带来系统卡顿\崩溃。
- 流量分层
对于瞬时流量可以采用流量分层的形式、逐层控制。
1. CDN 2. 反向代理 3. 后端服务 4. DB
流量分层控制
CDN层流量控制,提前生成尽可能多的数据放入CDN缓存当中,因为CDN离用户层比较近。
反向代理流量控制。可以通过定时任务把前端需要的内容提前分发的分发服务上去,内容风法服务将这些静态数据分发给所有的反向代理服务器。采用Nginx反向代理的时候会产生日志,在这期间需要考虑到恶意请求的存在。可以利用风控平台等工具规避这一内事件的发生。
后端服务流量层控制。
- 在程序开发上、代码独立、不要与平台其他项目混合。
- 在部署时,应用独立部署,分散流量,避免不适合的流量影响主体业务。
- 使用独立域名,或者安定一定的URL规则在反向代理层进行路由。
- 做好系统保护和限流、进一步减少不必要的流量。
扣减库存有哪几种方式?
- 下单后扣减库存 ,用户下单之后扣减库存。
- 支付后扣减库存,用户下单并且支付之后扣减库存。
- 预扣减库存,在用户下完订单后,系统会为其锁定库存一段时间。
如何在千万级的流量的秒杀中,如何扣减库存?
由于在秒杀当中,商品一般都对用户很具有吸引力,所以在这种情况下,采用下单后扣减库存的方式更加合适。
如何做好下单后扣减库存,防止商品超卖?
- 将某个商品的数量查询出来
- 更新库存(为什么是更新库存,因为扣减不是幂等的。如果接口设计不完善则会导致商品超卖,或者重复扣减的操作)
上面的操作真的没问题了吗?答案是NO!!在高并发的情况下,如果有2个用户同时抢购,都拿到了库存数量等于0的商品,其中一个用户购买了5件,一个用户购买了3件。随后更新库存的数量等于(10-5=5).接着另外一个请求购买了3件商品,随后更新库存数量为(10-3=7)。
优化方案:
在更新库存数量时,需要将当前的库存数量与之前的库存数量比较用户A和B只有在查询到库存数量为10的情况下,才能更新成功
update shop set num=$newnum where pro_id=$proId and num=$oldNum
在千万流量的秒杀中,优化库存数量扣减
1.在秒杀场景当中通过流量分层控制,可以管控大量的请求,依然会有很多请求落到后端服务当中去。所以还需要进一步优化
- 利用好缓存,可以先将库存数量缓存到缓存当中去。让缓存去迎接挑战。使用缓存也需要考虑其他问题,比如缓存出现异常,如何容错
- 异步处理 如果是复杂的扣减操作,则建议使用数据库进行库存的扣,可以使用异步的方式应对这种高并发的库存数量更新
- 在用户下单时并不生成真正的订单 ,将订单放入队列当中
- 下单模块依据自身的消费速度处理
- 在订单生成成功之后,用户即可执行支付。
搭建千万级秒杀系统所 使用到的技术
- 数据静态分离
- 多层层级的缓存处理
- 分布式缓存处理
- 负载均衡
- Nginx
- LVS
- 异步处理技术
- 消息队列技术
- 排队系统技术
- 系统架构设计与技术
- 微服务模块划分
- 微服务架构思想
- 系统监控技术
- 日志监控
- 服务监控
基础服务治理
什么是服务注册中心
服务注册中心是为微服务架构当中的一个重要组件,Nacos是由阿里开源的一套组件。Nacos致力于发现、配置和管理服务。
Nacos架构原理
- 服务提供者实例启动时,通过Nacos Server提供的OpenApi注册到服务注册表中
- 服务消费者通过OpenApi查询服务注册表,以查找服务的可用实例
- 服务提供者在实例关闭的时候,在注册表当中注销实例消息。
Nacos源码分析
- SpringCloud 提供一个服务注册的接口——ServiceRegistry接口,集成到Spring Cloud 中的、实现服务注册的组件都需要实现该接口,Nacos就是通过该接口实现的注册发现。Nacos实现的叫做NacosServiceRgistry。
- 源码当中的register方法实在AbstractAutoServiceRegistrain类中调用的。首先在bind()方法中调用start()方法,接着start()方法调用register方法。
RPC框架服务通信
RPC框架原理
- RPC框架提供了一个Client Stub组件,其本质上是一个代理类实例,客户端通过它发起方法调用
- Client Sub实现了服务端的接口,它会封装信息,主要分为2块,服务名称和参数
- Client Sub将上面封装好的信息传给服务端
- 服务端收到请求后,解析请求信息中的内容和服务名称 ,并判断服务端是否有该服务信息
- 如果在服务端请求找到服务时则调用服务端的接口处理
- 服务端封装响应信息返回给客户端
- 客户端收到响应处理。
RPC框架需要解决的问题
- 网络通信问题
- 客户端和服务端通过TCP连接通信,主要涉及I/O模型的选择
- 同步阻塞(BIO) 客户端发送请求后,该请求会在内核当中阻塞,直到服务端响应的数据返回才执行后续。
- 同步非阻塞(NIO) 在客户端发送请求时吗,在内核空间不会阻塞继续执行下面的操作,同步非阻塞需要频繁轮询 CPU资源消耗过大。
- I/O多路复用 在客户端每次发送请求时,服务端不需要每次都创建对应的线程去处理,而是通过多路复用技术,把多个I/O通道复用到一个复用器上,这样可以实现单线程处理多个客户端的请求 提高程序性能。
- 异步非阻塞(AIO) 在客户端发起I/O操作后,不需要等待直接返回,真正的I/O读写操作由操作系统内完成,不存在阻塞问题。AIO适用于连接数较多,请求消耗比较严重的业务场景上。
- 传输协议问题
- 客户端和服务端按照特定的契约执行编码解码操作。
- 序列化和反序列化问题。常用的结构有JSON\XML 因为这2种格式结构较为清洗
- 把对象转换为字节码叫做序列化
- 把字节码转换成对象叫做反序列化 。
分布式事务管理
- 数据一致性 数据执行一次或多次,能保持业务的不变性。导致数据不一致性的原因有很多:
- 用户读取到的数据不一致。读取的时候数据未同步。
- Leader节点同步到slave节点其中一个节点未同步,则会造成数据不一致。
- 在使用缓存的场景中,缓存更新策略的不同会造成与数据库不一致。
- 数据库事务保证一致性 利用数据库的ACID特性
- 分布式事务
- 基于XA协议的二阶段提交(常用)
- 向参与者发送准备指令,事务协调者收到所有执行者"准备完成"的指令
- 向参与者发送提交指令,然后各自发送 “提交成功”的反馈信息
- 基于XA协议的三阶段提交
- TCC
- 基于消息的最终一致 利用消息中间件达成最终的一致性。
- 订单系统创建订单信息,并且发送消息给RocketMQ,此时消息状态 "待确认"
- 消息在RocketMq当中被持久化存储,存储消息状态为"待发送"
- 如果消息被持久化成功,则订单开始创建订单。否则 废弃创建订单
- 订单系统在创建完订单后,将创建订单成功的消息发送给RocketMQ
- RocketMQ收到创建订单成功的消息,然后将消息状态设置为"可发送",接着RocketMQ把当前订单创建成功的消息发送给购物车系统
- 购物车系统获取订单成功消息,删除购物车 的商品
- 购物车系统完成自己的操作后,通知RocketMQ将当前创建订单成功的床单改为"已完成"
在分布式系统当中只有所有节点事务都成功,整个分布式事务才算成功。目前还没有完美的解决办法,只能根据业务场景去使用,选择合适的方案。