微服务多了之后的治理难题

服务从 3 个拆到 30 个之后,每个服务之间都要互相调用。问题来了:限流怎么做?熔断写在哪?金丝雀发布的 5% 流量怎么切?调用链的耗时怎么统计?如果这些逻辑都写在每个业务服务的代码里,就是每个团队都要自己实现一遍,而且各写各的——A 服务的重试策略和 B 服务不一样,出了问题排查都对不上。

服务网格(Service Mesh)想解决的就是这件事:把这些「流量治理」的共性能力,从业务代码里彻底抽出来,交给一层专门的基础设施。业务代码只写业务逻辑,流量怎么路由、失败了怎么重试、要不要熔断,全部交给网格统一管。

业务代码写「我要调用订单服务」;服务网格决定「这次请求走哪条路、超时了怎么办、失败几次就熔断」。
本文核心知识点
  • Sidecar每个服务旁边挂一个代理,所有进出流量都先过它。
  • 流量治理路由、重试、熔断、限流全部在代理这层配置。
  • 金丝雀按权重把一小部分流量切到新版本,观察没问题再放量。
  • Istio这个领域最主流的开源实现。
服务网格与流量治理配图:多个服务节点用细线交织成网状、中央流量分发器把流量按两条路径分流的蓝色扁平科技插画
所有服务之间的调用,都被收进一张统一的网里治理。

服务网格:把治理能力抽出业务代码

在没有服务网格的时候,熔断、重试、限流这些事通常有两种做法:要么写在业务代码里(每个语言一套 SDK),要么靠入口那层负载均衡做一部分。前者的问题是每个团队都要实现一遍、版本还对不齐;后者的问题是它只管入口那一跳,服务和服务之间的内部调用它管不着。

服务网格换了个思路:在每个服务实例旁边都放一个专门的网络代理,这个服务的所有进出流量都先走代理,再由代理转发出去。治理规则(重试几次、超时多少、要不要熔断)全部配置在这些代理上——业务代码完全无感知,换语言、换框架都不影响,因为治理逻辑不在代码里。

Sidecar:每个服务配一个代理

这个「贴在服务旁边的代理」叫 Sidecar(边车)。在 Kubernetes 上用 Istio,就是给每个 Pod 自动注入一个额外的代理容器,和你的业务容器并排跑在同一个 Pod 里:

一个 Pod 里:
├── 你的业务容器(只写业务逻辑)
└── Istio Sidecar 代理(所有流量的必经之路)

服务 A 调服务 B 的实际路径:
业务A → Sidecar(A) → 网络 → Sidecar(B) → 业务B

正因为所有流量都被 Sidecar 截获,治理才变得可能:超时、重试、熔断、限流、链路追踪,全都在 Sidecar 这一层统一实现。业务代码调远程服务的那行代码一个字不用改,治理策略却可以随时调整。Istio 控制面再把你写的规则下发到每个 Sidecar,形成一张统一治理的网。

流量路由:金丝雀发布怎么配

服务网格最常用的能力之一,就是按权重切流量,实现金丝雀发布。规则用 YAML 声明:把 90% 流量给旧版本、10% 给新版本,观察新版本没问题再逐步放量:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
  hosts:
  - web
  http:
  - route:
    - destination:
        host: web
        subset: stable
      weight: 90
    - destination:
        host: web
        subset: canary
      weight: 10

这个配置一改,流量比例立刻生效,不需要改任何业务代码、也不需要重新部署。除了按权重,还能按请求特征分流:比如「带某个请求头的内部测试用户,全部导到新版本」——这比在业务代码里写灰度判断干净得多。配合上一篇讲的 GitOps,切流量这个动作本身也是一次 Git 评审。