专栏名称: 携程技术
携程技术官方账号,分享交流成长。
目录
相关文章推荐
济南时报  ·  刚刚,2025年济南中考作文题目出炉 ·  5 小时前  
城市数据团  ·  【高考季】用数据和AI重新定义高考志愿推荐 ·  昨天  
51好读  ›  专栏  ›  携程技术

干货 | 携程 SOA 的 Service Mesh 架构落地

携程技术  · 公众号  ·  · 2022-05-12 17:00

正文

请到「今天看啥」查看全文


descriptors:
  - entries:
      - key: client_appid
        value: client-a
    token_bucket:
      max_tokens: 10
      tokens_per_fill: 10
      fill_interval: 60s
  - entries:
      - key: client_appid
        value: client-b
      - key: path
        value: /foo
    token_bucket:
      max_tokens: 100
      tokens_per_fill: 100
      fill_interval: 60s

这里的 client_appid 和 path 的值,通过如下在 route 的 ratelimit 配置中添加 actions 来设置。

route:
  cluster: service_protected_by_rate_limit
  rate_limits:
    - actions:
        - request_headers:
            header_name: x-envoy-client-appid
            descriptor_key: client_appid
        - request_headers:
            header_name: ":path"
            descriptor_key: path

以上配置本质上就是通过从请求中提取一些特征,例如读取一些特定的 Header,然后再针对不同的请求分配不同的限流值。

对于更加复杂的场景,例如需要根据多个 Header 做逻辑判断时,我们通过 Envoy Filter 实现相关逻辑并设置到 metadata 中。然后在 ratelimit 的 actions 中从 metadata 中提取特征。例如,我们可以 patch 如下的配置到 envoy 中,生成限流使用的 metadata 数据。

- applyTo: HTTP_FILTER
  match:
    context: SIDECAR_INBOUND
    listener:
      filterChain:
        filter:
          name: envoy.filters.network.http_connection_manager
          subFilter:
            name: istio.metadata_exchange
  patch:
    operation: INSERT_FIRST
    value:
      name: service.metadata.ratelimit
      typed_config:
        '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
        inline_code: |-
          function envoy_on_request(request_handle)
              custom_key_1 = build_custom_key_1()
              custom_key_2 = build_custom_key_2()
              request_handle:streamInfo():dynamicMetadata():set("metadata.custom.ratelimit", "request.info", {
                  customKey1 = custom_key_1,
                  customKey2 = custom_key_2
              })
          end

然后通过 metadata_key 的方式,拿到我们自己设置的限流相关的 metadata。

route:
  cluster: inbound|8080|http|svc-a
  rate_limits:
    - actions:
        - metadata:
            descriptor_key: customKey1
            metadata_key:
              key: metadata.custom.ratelimit
              path:
                - key: request.info
                - key: customKey1
        - metadata:
            descriptor_key: customKey2
            metadata_key:
              key: metadata.custom.ratelimit
              path:
                - key: request.info
                - key: customKey2

其他功能

除了一些服务治理常见的功能,携程内部也有不少定制化的功能需要在 Service Mesh 中实现。

这部分需求部分是通过 Lua Filter 实现,部分是扩展 Envoy 编写了 C++ Filter 来实现的。

因为这 C++ Filter 这部分需求比较稳定,所以静态编译在 Sidecar 中也并不是个大问题。

如果可以通过 WebAssembly 实现当然是可以做得更灵活。但在我们项目启动的时候,Istio 的 WebAssembly 还未正式发布,后期我们会考虑引入 WebAssembly。

3.4 SDK 兼容

接入 Service Mesh 后的一大优势就是可以为 SOA SDK 做轻量化,仅保留基本的功能即可。

而 Service Mesh 的接入是一个长期的过程,应用是一批批接入的,同一个应用在不同的机房也有可能存在接入和不接入两种状态。让业务方写两个版本的代码肯定是不合适的。

因此我们在现有的 SOA SDK 中实现了无缝接入功能,原理也非常简单。

凡是接入 Service Mesh 的应用在发布时就会被注入一个环境变量,当 SOA SDK 探测到这个环境变量后,便会启动轻量化模式。

其中轻量化模式中被移出的功能包括:

  • • 禁用服务注册与发现

  • • 禁用路由功能,每个服务都有一个固定的域名,所有请求直接按服务请求固定的域名即可

  • • 禁用各种服务治理功能,例如:熔断、限流、黑白名单、重试、负载均衡、路由等

这样不仅有利于业务方快速回滚,也可以方便业务方对两种 SOA 架构进行性能对比。

四、数据平面

4.1 HTTP 协议

携程当前主流的 SOA 传输协议还是 HTTP,这块的确慢了半拍,但这也更利于我们接入 Service Mesh。

Istio 本身对 HTTP 协议有着很好的支持,因此这部分并不需要我们做什么调整。

4.2 Dubbo 协议

携程在 2018 年的时候引入了 dubbo 协议作为 HTTP 协议的补充,在两年时间的发展中也积累了较多的用户群体。

我们的 dubbo 的调用方式依赖 Dubbo 框架中的 dubbo 协议。

部分应用扩展使用了压缩率更高的 protobuf 来做序列化并进一步使用 gzip 压缩提升性能。

dubbo 协议本身是四层的私有协议,在 Istio 中的支持力度远不如 HTTP。另外 Dubbo 3.0 中也将 gRPC 协议作为了新的传输协议。

如何让 dubbo 协议升级到 gRPC 成为 Service Mesh 落地必须解决的问题。

现状

当前携程内部通过 Dubbo 框架调开发服务端应用有近千个,对应的调用方就更多了。

并且考虑到携程内部使用 Node.js、Python 等语言的应用越来越多,新版升级必须满足如下的要求:

1)使用 gRPC 协议以支持 Service Mesh

2)使用 Dubbo 框架的业务方尽量不改或者少改代码

3)新协议注册的服务端符合 gRPC 的规范并使得其他语言客户端可以很方便地调用

4)不强制依赖 protobuf,能让用户保持 Code First 的编码习惯

技术选型

我们调研了当时主流的做法并通尝试得出三条可行的升级道路。

这里最大的难点是当前使用 dubbo 的旧服务不是基于 protobuf 编写契约的,所以不能直接通过依赖 protobuf 结构的 gRPC 发起调用。

【方案一】

依然用原来的序列化器将数据处理成二进制,在 gRPC 调用时 wrap 一个 protobuf 对象,用一个字段传递原来数据的二进制数据流,再用另外一些字段描述它的序列化方式。这也是 Dubbo 3.0 中透明升级 gRPC 协议时所使用的方案。

  • • 优点:

    • • 该方案不需要对业务代码改动,并且支持 Code First

  • • 缺点:

    • • 这种方式对于标准的 gRPC 客户端不友好

    • • 两次序列化和反序列化影响性能

【方案二】

gRPC 标准中,没有规定 gRPC 强依赖 protobuf 做序列化器,gRPC 官方的 FAQ 中这样写道:

gRPC is designed to be extensible to support multiple content types. The initial release contains support for Protobuf and with external support for other content types such as FlatBuffers and Thrift, at varying levels of maturity.

那具体如何使用其他序列化器呢?

从协议角度,只需要改变 content-type 即可。例如想表达用 json 作为序列化格式,那具体内容为: application/grpc+json







请到「今天看啥」查看全文