大型分布式系统在实际运行过程中面对的工况是非常复杂的,业务流量的突增、依赖服务的不稳定、应用自身的瓶颈、物理资源的损坏等方方面面都会对系统的运行带来大大小小的的冲击。
流量削峰
对于秒杀的目标场景,最终能够抢到商品的人数是固定的,无论 100 人和 10000 人参加结果都是一样的,即有效请求额度是有限的。
并发度越高,无效请求也就越多。但秒杀作为一种商业营销手段,活动开始之前是希望有更多的人来刷页面,只是真正开始后,秒杀请求不是越多越好。因此系统可以设计一些规则,人为的延缓秒杀请求,甚至可以过滤掉一些无效请求。
答题
通过答题提升购买的复杂度防止作弊和延缓请求。
- 防止作弊:存在恶意买家或竞争对手使用秒杀器扫货的情况,商家没有达到营销的目的,所以增加答题来进行限制
- 延缓请求:零点流量的起效时间是毫秒级的,答题可以人为拉长峰值下单的时长,由之前的 <1s 延长到 <10s。这个时间对于服务端非常重要,会大大减轻高峰期并发压力。由于请求具有先后顺序,答题后置的请求到来时可能已经没有库存了,因此根本无法下单,此阶段落到数据层真正的写也就非常有限了。
排队
通过把同步的直接调用转换成异步的间接推送缓冲瞬时流量。
- 消息队列
- 线程池加锁等待
- 本地内存蓄洪等待
- 本地文件序列化写,再顺序读
排队方式的弊端也是显而易见
- 请求积压。流量高峰如果长时间持续,达到了队列的水位上限,队列同样会被压垮,这样虽然保护了下游系统,但是和请求直接丢弃也没多大区别
- 用户体验。异步推送的实时性和有序性自然是比不上同步调用的,由此可能出现请求先发后至的情况,影响部分敏感用户的购物体验。
过滤
过滤的核心结构在于分层,通过在不同层次过滤掉无效请求,达到数据读写的精准触发。过滤的核心目的是通过减少无效请求的数据 IO 保障有效请求的 IO 性能。
常见的过滤主要有以下几层:
- 读限流:对读请求做限流保护,将超出系统承载能力的请求过滤掉。
- 读缓存:对读请求做数据缓存,将重复的请求过滤掉。
- 写限流:对写请求做限流保护,将超出系统承载能力的请求过滤掉。
- 写校验:对写请求做一致性校验,只保留最终的有效数据。
系统建设
- 架构阶段:考虑系统的可扩展性和容错性,避免出现单点问题。例如多地单元化部署,即使某个 IDC 甚至地市出现故障,仍不会影响系统运转
- 编码阶段:保证代码的健壮性,例如 RPC 调用时,设置合理的超时退出机制,防止被其他系统拖垮,同时也要对无法预料的返回错误进行默认的处理
- 测试阶段:保证 CI 的覆盖度以及 Sonar 的容错率,对基础质量进行二次校验,并定期产出整体质量的趋势报告
- 发布阶段:系统部署最容易暴露错误,因此要有前置的 checklist 模版、中置的上下游周知机制以及后置的回滚机制
- 运行阶段:系统多数时间处于运行态,最重要的是运行时的实时监控,及时发现问题、准确报警并能提供详细数据,以便排查问题
- 故障发生:首要目标是及时止损,防止影响面扩大,然后定位原因、解决问题,最后恢复服务。
对于日常运维而言,高可用更多是针对运行阶段而言的,此阶段需要额外进行加强建设,主要有以下几种手段:
- 预防:建立常态压测体系,定期对服务进行单点压测以及全链路压测,摸排水位
- 管控:做好线上运行的降级、限流和熔断保护。需要注意的是,无论是限流、降级还是熔断,对业务都是有损的,所以在进行操作前,一定要和上下游业务确认好再进行。就拿限流来说,哪些业务可以限、什么情况下限、限流时间多长、什么情况下进行恢复,都要和业务方反复确认
- 监控:建立性能基线,记录性能的变化趋势;建立报警体系,发现问题及时预警
- 恢复:遇到故障能够及时止损,并提供快速的数据订正工具,不一定要好,但一定要有