Go 微服务可用性设计

本文最后更新于:2 年前

Go 微服务可用性设计知识点

隔离

服务隔离

动静隔离

如 CND 缓存加速、mysql LRU 算法改进

业务可以拆成两张表,静态表(字段机会不会改)和动态表(字段经常改)

读写隔离

  • 主从
  • CQRS

轻重隔离

  • 核心:服务按核心与否进行分离,越核心的服务可用性要求也高
  • 快慢
  • 热点
    • 小表广播: 存为到内存,定期更新
    • 主动预热:旁路预热

物理隔离

  • 线程:使用线程池,不同业务逻辑用不同的池子
  • 进程:容器化
  • 集群:region.zone.cluster.appid
  • 机房

超时控制

核心理念: fail fast

起因

  • 网络具有不确定性
  • 客户端和服务端不一致的超时策略

方案

  • 进程内:每一个请求前查看是否有剩余时间,并继承超时策略
  • 服务间: GRPC 支持基于 grpc-timeout 的 Header 传递
  • 监控要看 95th、99th

过载保护

常见限流算法

  • 令牌桶:按固定速率往桶里加令牌,可以消费多个令牌
  • 漏斗桶: 按固定速率流出水滴,可以以任意速率流入水滴

痛点:阈值不好设置

自适应过载保护

inflight: 当前服务正在进行的请求数量(atomic.add 来加减)
pass: 每个采样窗口内成功的请求数
rt: 单个采样窗口的平均 响应时间

CPU 统计:使用独立的 goroutine 每隔 250ms 统计一次,计算均值时,使用简单滑动平均(考虑前一次的均值)去除峰值的影响

统计 CPU 的滑动均值作为启发阈值(如 80%),一旦触发进入过载保护阶段,算法:(pass * rt) < inflight

另外过载保护需要保证持续最小时间如 2s,以避免一个短时间的 CPU 下降可能导致大量请求被放行,严重是会打满 CPU

限流

定义:在一段时间内某个客户或应用可以处理多少个请求

作用:过滤流量峰值

常规方案

  • 令牌桶:针对单个节点无法分布式限流
  • QPS 限流:
    • 不同的请求可能消耗的资源完全不同
    • 静态阈值很难设置准确
  • 使用 redis 分布式限流
    • 单个大流量接口容易产生热点 key
    • 每次请求需要拿一个 quota –> 优化:基于历史窗口数据批量拿 quota,减少请求 redis 频次

资源分配:Max-Min Fairness 算法

重要性 criticality

  • 将请求按重要性分级,配额不足时,先拒绝优先级低的
    • critical_plus
    • critical
    • sheddable_plus
    • sheddable
  • 通常 BFF 层接口分级,重要性可往下游传递

熔断: 客户端自适应节流

请求拒绝率: max(0, requests - K * accepts/(requests+1))

客户端流控

作用:避免 positive feedback 积极重试访问不可达的服务

  • 限制请求频次,重试添加 backoff 退让策略

Gutter

gutter 集群只需要主集群的 10%的资源
主集群发生熔断被抛弃的请求会转移到 gutter 集群
如果 gutter 集群也接受不住流量,会重现抛到主集群

降级

本质为提供有损服务
通过降低回复的质量来答复以减少所需的计算量或时间

通常采用错误或者 CPU 指标作为决定性指标

通常在 BFF 层或网关来做

重试

随机化+指数避让

只应该在失败层重试,以免指数放大次数,如收到 503 就表示下游已重试过则直接往上抛结果当前层不在重试了

业务不幂等:尽量不重试,即使重试也需要避免数据发生重复

负载均衡

目标

  • 均衡流量分发
  • 识别异常节点
  • 水平扩容
  • 高可用 N+2

可能的问题

  • 不同节点负载不一样
    • 原因:
      • 不同请求处理成本不一样
      • 机器性能不一样
      • 可能有节点 FullGC 中
      • 多个 LB 中每个 LB 都没有全局视角

Power of Two Choices (P2C)

随机请两个节点进行打分,选择更优的节点

打分:服务端 CPU(通过 rpc response head 获取)、耗时、成功率,inflight

新加入节点开始权重较小,这个特性尤其对 Java 服务比较友好

指标计算结合滑动均值且使用时间衰减

最佳实践

变更管理:

70%的问题是由变更引起的,恢复可用代码并不总是坏事。

避免过载:

过载保护、流量调度等。

依赖管理:

任何依赖都可能故障,做 chaos monkey testing,注入故障测试。

优雅降级:

有损服务,避免核心链路依赖故障。

重试退避:

退让算法,冻结时间,API retry detail 控制策略。

超时控制:

进程内 + 服务间 超时控制。

极限压测 + 故障演练。

扩容 + 重启 + 消除有害流量。

references