代理架构:把分布式系统的"杂活"从业务代码里剥离出去

2026-06-05 24 预计阅读时间:1 分钟
来源:my.oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:11 分钟

写分布式服务的人都有过这种体验:一个接口逻辑只有二十行,但前后各塞了十行——鉴权、限流、日志、重试、熔断。业务代码被横切关注点淹没,改一个路由规则要重新部署三个服务。代理架构要解决的就是这个问题:让组件只管业务,通信协调和系统质量相关的活交给代理。

代理在分布式系统中的角色

代理架构的核心思路很简单——组件之间不直接调用,而是通过代理间接交互。代理坐在这条链路上,既转发请求,也拦截请求。它不是简单的"传话筒",而是一个具备决策能力的中间层。

典型拓扑是这样的:

Service A → Proxy → Proxy → Service B

每个组件只知道自己要跟哪个代理对话,不知道对方的真实地址。代理之间可以组成网格,也可以集中为网关形态。无论哪种部署形式,代理承担的职责是一致的:

  • 路由:把请求导向正确的实例或版本,支持灰度、流量拆分
  • 安全:TLS 终止、身份校验、访问控制,业务服务不再自己处理证书轮换
  • 熔断与容错:下游异常时快速失败,避免级联雪崩
  • 缓存:对高频读请求做响应缓存,减轻后端压力
  • 日志与可观测性:统一采集调用链路指标,业务服务零侵入接入监控

这些功能如果写在业务代码里,每个服务都要重复实现一遍。代理把它们抽出来,变成基础设施能力。

两种主流部署形态

代理架构落地时,通常走向两种形态,各有适用场景:

集中式网关——所有外部流量先经过一个代理集群再进入内部服务。适合南北向流量(客户端到服务端),典型实现是 Kong、Nginx Ingress Controller。优点是管控集中、配置一处生效;缺点是网关成为单点,规模大时需要仔细做高可用。

分布式 Sidecar——每个服务实例旁部署一个轻量代理,服务间调用都经过各自的 Sidecar。适合东西向流量(服务到服务),典型实现是 Istio 的 Envoy Sidecar。优点是无单点、策略可按服务细粒度定制;缺点是代理数量多,运维和资源开销更高。

实际系统中两种形态经常共存:网关挡外部流量,Sidecar 管内部网格。

用 Envoy Sidecar 搭一个最小代理层

下面用一个 Kubernetes + Envoy 的最小示例,展示代理架构怎么把鉴权、路由、熔断从业务代码里拿走。假设你有两个服务:order-servicepayment-service,订单服务调用支付服务。我们给订单服务加一个 Envoy Sidecar 代理,让它接管所有出站流量。

先定义 Envoy 的配置——这是代理架构中最核心的文件,所有路由、熔断、安全策略都在这里声明:

# envoy-config.yaml — Envoy Sidecar 配置
static_resources:
  listeners:
    - name: outbound_listener
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 15001
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: outbound
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: payment_service
                      domains: ["payment-service"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: payment_cluster
                            timeout: 3s          # 请求超时,替代业务代码里的 retry timeout
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
    - name: payment_cluster
      type: STRICT_DNS
      connect_timeout: 1s
      lb_policy: ROUND_ROBIN
      circuit_breakers:                          # 熔断配置,替代业务代码里的 circuit breaker
        thresholds:
          - priority: DEFAULT
            max_connections: 100
            max_pending_requests: 50
            max_requests: 200
            max_retries: 3
      outlier_detection:                         # 异常实例自动摘除
        consecutive_5xx: 3
        interval: 10s
        base_ejection_time: 30s
      load_assignment:
        cluster_name: payment_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: payment-service
                      port_value: 8080

这份配置做了三件业务代码不再需要做的事:

  1. 路由:访问 payment-service 的请求全部走 payment_cluster,业务代码只需用服务名发起调用,不用知道真实 IP。
  2. 超时与熔断:3 秒请求超时、100 连接上限、连续 3 次 5xx 自动摘除实例——这些参数原来要写在每个服务的 SDK 里。
  3. 负载均衡:ROUND_ROBIN 策略由代理执行,业务侧无需引入客户端负载均衡库。

接下来把 Envoy 作为 Sidecar 部署到订单服务所在的 Pod:

# order-deployment.yaml — 带 Sidecar 的订单服务部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        # 业务容器:只管订单逻辑
        - name: order-app
          image: order-service:v1
          env:
            - name: PAYMENT_URL
              value: "http://127.0.0.1:15001"   # 出站流量走本地 Sidecar
          ports:
            - containerPort: 8080
        # 代理容器:接管通信、安全、熔断
        - name: envoy-sidecar
          image: envoyproxy/envoy:v1.28-latest
          volumeMounts:
            - name: envoy-config
              mountPath: /etc/envoy
              readOnly: true
      volumes:
        - name: envoy-config
          configMap:
            name: envoy-outbound-config

部署到集群:

# 创建 ConfigMap 存放 Envoy 配置
kubectl create configmap envoy-outbound-config \
  --from-file=envoy.yaml=envoy-config.yaml \
  -n default

# 部署带 Sidecar 的订单服务
kubectl apply -f order-deployment.yaml -n default

# 验证 Sidecar 正常启动
kubectl logs deployment/order-service -c envoy-sidecar -n default | head -5
# 期望看到:starting main dispatch loop

关键变化在 PAYMENT_URL 这个环境变量:业务代码不再直连支付服务,而是请求本地 Sidecar 的 15001 端口。Envoy 根据配置中的路由规则,把请求转发到真实的 payment-service:8080。业务代码里所有关于重试、超时、熔断的逻辑都可以删掉——代理已经接管了。

如果后续要加鉴权,只需在 Envoy 配置里增加一个 envoy.filters.http.ext_authz 过滤器指向外部鉴权服务,业务代码一行不用改:

# 在 http_filters 中插入鉴权过滤器(加在 router 之前)
http_filters:
  - name: envoy.filters.http.ext_authz
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
      grpc_service:
        envoy_grpc:
          cluster_name: auth_cluster
        timeout: 0.5s
  - name: envoy.filters.http.router

代理架构的代价与取舍

代理不是免费的午餐,引入前要算清楚几笔账:

延迟增加。每多一层代理就多一次网络跳转,Sidecar 模式下本地调用额外增加约 1-2ms。对延迟敏感的场景(高频交易、实时游戏)要评估是否可接受。

运维复杂度上升。集中式网关要管高可用和容量规划;Sidecar 模式下代理数量等于服务实例数,配置变更的滚动生效需要自动化支撑。Istio 的控制平面正是为此而生——用 Pilot 统一下发配置,避免手动管理数百份 Envoy YAML。

调试链路变长。请求从 A → Proxy → Proxy → B,排查问题时需要穿过代理层查看日志和指标。好在代理本身是可观测性的天然采集点——Envoy 暴露的 /stats 端点和访问日志,反而让全链路追踪比裸调用更容易。

功能边界。代理适合处理与通信相关的横切关注点,不适合承载业务逻辑。如果代理里开始写条件分支做订单金额校验,那就是在把业务塞回基础设施,方向反了。

落地前的检查清单

决定引入代理架构之前,逐项确认:

  • ✅ 是否有三个以上服务存在重复的鉴权/限流/熔断/日志代码?——这是代理最直接的收益点
  • ✅ 延迟预算是否允许额外 1-2ms 跳转?——不允许的话考虑集中式网关(跳转少一次)或绕过代理的直连通道
  • ✅ 配置管理是否有自动化方案?——手动维护几十份代理配置会很快失控
  • ✅ 团队是否具备代理运维能力?——Envoy/Nginx 的调优、故障排查需要专门知识
  • ✅ 是否明确了代理与业务逻辑的边界?——代理只管通信质量和安全,不碰业务规则

代理架构的本质是职责分离:让业务开发者专注领域逻辑,让基础设施团队统一管控通信质量。它不是万能解,但在服务数量超过一定规模、横切代码开始到处复制的时候,是把系统从混乱拉回秩序的有效手段。


相关推荐