一、介绍1.1KitexProxylessKitex是字节跳动开源的GolangRPC框架。已经原生支持xDS标准协议,支持Proxyless被ServiceMesh统一管理使用。详细设计见:提案:Kitex支持xDS协议·Issue#461·cloudwego/kitex(https://github.com/cloudwego/kitex/issues/461)具体使用方法见:官方文档(https://github.com/cloudwego/kitex/issues/461)/www.cloudwego.io/zh/docs/kitex/tutorials/advanced-feature/xds/)KitexProxyless简单的说就是Kitex服务可以直接和istiod交互,不需要envoysidecar,动态获取控制面下发的服务治理规则基于xDS协议,并转换成Kitex相应的规则,实现一些服务治理功能(比如本文的重点:流量路由)。基于KitexProxyless,让我们实现Kitex无需代理即可由ServiceMesh统一管理,进而实现多种部署模式下治理规则Spec、治理控制面、治理交付协议、异构数据治理能力的统一。1.2流量路由流量路由是指根据自身特定的元数据标识,将流量路由到指定目的地的能力。流量路由是服务治理的核心能力之一,也是KitexProxyless支持的首选场景之一。Kitex基于xDS实现流量路由的方案大致如下:具体过程:添加一个xDSRouterMW负责PickCluster(路由),watch目标服务的LDS和RDS。感知LDS的变化,提取目标服务LDS中的FilterChain及其内联RDS。感知RDS变化,根据VirtualHost和ServiceName(支持前缀、后缀、exact、wildcard)进行匹配,获取目标服务的路由配置。遍历并处理RDS中匹配的路由规则。路由规则主要分为两部分(参考:路由规范定义):匹配(支持前缀、后缀、精确、通配符等),当前版本我们支持以下两种Path(必填):ExtractMethodfromrpcinfo用于匹配。HeaderMatcher(可选):从元信息中提取对应的元数据KeyValue,进行匹配。路线:集群:标准集群。WeightedClusters(权重路由):根据MW中的权重选择簇。将选中的Cluster写入EndpointInfo.Tag中,用于后续的服务发现。可见,流量路由其实就是按照一定的规则选择对应的SubCluster的过程。2、全链路泳道基于流量路由能力,我们可以扩展很多使用场景,比如:A/B测试、金丝雀发布、蓝绿发布等,本文重点:全链路泳道。全链路泳道可以理解为将一组服务实例按照一定的方式(比如部署环境)进行拆分,基于全链路灰度路由能力,流量可以按照以下方式在指定的服务实例泳道中流动规则(逻辑就像游泳池中的泳道)。在Istio中,我们一般通过DestinationRule的子集对实例进行分组,将一个服务拆分成不同的子集(例如:根据版本、地域等属性进行拆分),然后配合VirtualService定义相应的路由规则,将流量路由到相应的子集子集,从而完成泳道中的单跳路由功能。但是单靠流量路由能力是不足以实现全链路泳道的,因为当一个请求跨越多个服务时,我们需要一个更好的机制来准确识别流量,并基于这个特性来服务每一个Jump流量配置路由规则。如下图所示:假设我们要实现一个用户的请求可以准确灰度到service-b的v1版本。首先想到的可能是所有的请求都有一个uid=100的请求头,然后根据header中的uid=100配置对应的VirtualService进行匹配。但是,这种方式有几个明显的缺点:不够通用:要使用特定的业务属性标识(如:uid)作为流量路由匹配规则,我们需要在整个链路中手动透传这个业务属性,这本身就是对业务的侵入性较大,需要业务合作和改造。而当我们要使用其他业务属性时,我们需要对整个链路业务进行再次改造。可想而知,这是一种非常不常见的做法。路由规则容易频繁变化,容易造成规则臃肿:使用特定的业务属性标识(如:uid)作为流量路由匹配规则。假设我们要改变一个业务属性,或者在为其他用户设置路由规则时,我们不得不修改原有的路由规则,或者针对不同的业务属性重复定义多套路由规则,很容易导致路由臃肿,使得很难维护。因此,为了实现全链路的统一流量路由,我们还需要使用更通用的流量着色和着色识别全链路透传能力。2.1流量着色流量着色是指给请求流量打上一个特殊的标记,并在整个请求链路上携带这个标记,所谓全链路泳道是指整个链路基于一个统一的灰度流量着色标记来设置流量路由规则,使流量在不同的泳道上都能得到准确的控制。通常,我们会在网关层进行流量着色,通常是根据原始请求中的元数据,将一定的规则(条件、比例)转化为相应的着色标志。按条件着色:当请求元数据满足一定条件时,会对当前请求进行着色标记,如:请求头中uid=100、cookie匹配等按比例着色:按照一定比例,标记着色标记在请求上。有了统一的流量着色机制,我们在配置路由规则时,不需要关心具体的业务属性标识,只需要根据着色标识进行配置即可。将具体的业务属性抽象为条件着色规则,使其更加通用。即使业务属性发生变化,也不需要频繁更改路由规则。2.2Dyeinglogo全链路透传Dyeinglogo通常依赖TracingBaggage进行透传。Baggage用于在整个链路中传递业务定义的KV属性,比如传递流量染色标识,传递AccountID等业务标识等。为了实现流染标识在全链路的透传,我们通常采用TracingBaggage机制,在全链路传输相应的染标识。大部分tracing框架都支持Baggage概念机能力,例如:OpenTelemetry、Skywalking、Jaeger等。采用通用的全链路透传机制,业务方只需要接入tracing一次,无需配合每次业务属性标识符更改时的转换。下面将通过一个具体的项目案例介绍来介绍和演示如何基于KitexProxyless和OpenTelemetryBaggage实现全链路泳道功能。3、案例介绍:Bookinfo本案例是使用Hertz和Kitex重写经典的IstioBookinfo项目:使用istiod作为xDS服务器,作为CRD配置和交付的入口;使用wire实现依赖注入;使用opentelemetry实现全链条道路追踪;使用Kitex-xds和opentelemetrybaggage实现proxyless模式下的全链路泳道;使用arco-design和react实现一个BookinfoUI。3.1架构整体架构与Bookinfo一致,分为四个独立的微服务:productpage。这个微服务会调用两个微服务,details和reviews;details。该微服务包含书籍信息;评论。此微服务包含与图书相关的评论。它还调用评级微服务;评级。该微服务包含由书评组成的评级信息。reviews微服务有3个版本:v1版本会调用ratings服务,用1??显示评分;v2版本将调用评分服务并使用5????????????来显示评分;v3版本不会调用评级服务。3.2泳道示意图整个区域分为2条泳道:BaseLane:未着色的交通将被引导至BaseLane。分支车道:彩色流量将被路由到reviews-v2->ratings-v2的分支车道。3.3流量着色网关负责对流量进行统一的着色。比如请求头中uid=100的流量统一上色,并在请求中携带env=dev包袱。染色方法可根据不同关口选择。比如我们选择istioingress作为网关,我们可以通过EnvoyFilter+Lua的方式编写网关染色规则。3.4为相应的工作负载用相应的版本标识符标记服务实例。以reviews为例,只需要在对应的pod上打上version:v1的标签即可。apiVersion:apps/v1kind:Deploymentmetadata:labels:app.kubernetes.io/instance:reviewsapp.kubernetes.io/name:reviews名称:reviews-v1spec:选择器:matchLabels:app.kubernetes.io/instance:reviewsapp.kubernetes.io/name:reviewsversion:v1template:metadata:annotations:sidecar.istio.io/inject:"false"labels:app.kubernetes.io/instance:reviewsapp.kubernetes.io/name:reviewsversion:v1。..基于DestinationRule为服务设置一系列的子集:Productpage:v1Reviews:v1、v2、v3Ratings:v1、v2apiVersion:networking.istio.io/v1alpha3kind:DestinationRulemetadata:name:productpagespec:host:productpagesubsets:-name:v1labels:版本:v1---apiVersion:networking.istio.io/v1alpha3kind:DestinationRulemetadata:名称:reviewsspec:主机:评论子集:-名称:v1标签:版本:v1-名称:v2标签:版本:v2-名称:v3标签:版本:v3---apiVersion:networking.istio.io/v1alpha3kind:DestinationRulemetadata:name:ratingsspec:host:ratingssubsets:-name:v1labels:version:v1-name:v2labels:version:v23.5流量路由规则网关已经请求头中携带uid=100的流量被染色,自动携带env=dev的包袱,所以我们只需要根据header进行路由匹配即可。下面是具体的路由规则配置示例:apiVersion:networking.istio.io/v1alpha3kind:VirtualServicemetadata:name:reviewsspec:hosts:-reviewshttp:-match:-headers:baggage:exact:"env=dev"route:-目的地:主机:评论子集:v2权重:100-路线:-目的地:主机:评论子集:v1权重:80-目的地:主机:评论子集:v3权重:20---apiVersion:networking.istio.io/v1alpha3kind:VirtualServicemetadata:name:ratingsspec:hosts:-ratingshttp:-match:-headers:baggage:exact:"env=dev"route:-destination:host:ratingssubset:v2weight:100-route:-destination:host:ratingssubset:v1weight:1003.6检查效果3.6.1Baseswimlaneentrytrafficrequestheader没有uid=100的请求会自动路由到benchmarkswimlane服务,在reviewsv1和v3服务之间轮询,显示效果是随机的3.6.2branchswimlane,分数为0或1。这里我们使用浏览器mod-header插件,模拟入口流量请求头携带uid=100的场景。2、再次点击刷新按钮,可以发现请求命中了分支泳道,流泳道功能成功生效。4.总结与展望至此我们已经实现了一个完整的基于KitexProxyless和OpenTelemetry的全链路泳道,并且可以在不使用Envoysidecar的情况下,基于Isito标准治理规则Spec为Kitex设置相应的路由规则。当然,除了满足流量路由能力,KitexProxyless也在不断迭代优化,以满足更多数据面的治理能力。Proxyless作为ServiceMesh数据面的一种探索与实践,不仅丰富了网格数据面的部署形式,也希望不断打磨Kitex,增强其在开源生态兼容能力,打造开放包容的微服务生态。5.相关项目链接以下是本案例涉及的项目列表:biz-demo:https://github.com/cloudwego/biz-demokitex:https://github.com/cloudwego/kitexhertz:https://github。com/cloudwego/hertzkitex-xds:https://github.com/kitex-contrib/xdskitex-opentelemetry:https://github.com/kitex-contrib/obs-opentelemetryhertz-opentelemetry:https://github.com/hertz-contrib/obs-opentelemetry的完整案例已经提交到biz-demo(https://github.com/cloudwego/biz-demo)仓库,有兴趣的同学可以去看看。biz-demo将包含一些基于CloudWeGo技术栈和特定业务场景的完整演示。初衷是为企业用户在生产中使用提供有价值的参考。欢迎更多同学参与到CloudWeGo相关场景和案例的贡献中来,一起来做一些有趣的实验。