likes
comments
collection
share

Dubbo3源码(五)服务网格

作者站长头像
站长
· 阅读数 66

前言

本章基于dubbo3.2.0分析dubbo对于服务网格的相关实现。

在3.1版本官方提供了Mesh相关特性,目前实现分为两种:

  • Sidecar Mesh:传统基于Envoy Sidecar代理方式,管理网络流量;
  • Proxyless Mesh:Dubbo自己实现xDS协议与istio通讯,进行服务发现;

注:对于Istio、Envoy、xDS细节本文不会深入分析,侧重点在于Dubbo如何接入ServiceMesh标准。

Sidecar(Proxy) Mesh

案例

提供者

提供者业务代码。

@SpringBootApplication
@EnableDubbo
@DubboService
public class App implements DemoService {

    @Value("${BIZ_VERSION:none}")
    private String bizVersion;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Override
    public String sayHello(String name) {
        return bizVersion + ", Hello " + name + ", response from provider: " + RpcContext.getServiceContext().getLocalAddress();
    }
}

提供者application.properties。

关键点:没有配置dubbo.registry注册中心

spring.application.name=dubbo-demo-proxy-mesh-provider
dubbo.protocol.name=tri
dubbo.protocol.port=50052
dubbo.application.name=${spring.application.name}

消费者

消费者业务代码。

关键点:DubboReference需要指定rpc服务提供者应用,这和使用k8s注册中心一致;

@SpringBootApplication
@EnableDubbo
@RestController
public class App {
    @DubboReference(
        check = false,
        providedBy = "dubbo-demo-proxy-mesh-provider"
    )
    private DemoService demoService;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @GetMapping("/invoke")
    public String invokeHttp() {
        return demoService.sayHello("Proxy Mesh");
    }
}

消费者application.properties。

关键点:

1)不需要指定注册中心;

2)设置meshEnable=true;

spring.application.name=dubbo-demo-proxy-mesh-consumer
dubbo.protocol.name=tri
dubbo.application.name=${spring.application.name}
dubbo.consumer.meshEnable=true

部署

消费者Deployment。

注意,这里环境变量里注入POD_NAMESPACE对应dubbo-demo,否则默认会走default命名空间。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dubbo-demo-proxy-mesh-consumer
  namespace: dubbo-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dubbo-demo-proxy-mesh-consumer
  template:
    metadata:
      labels:
        app: dubbo-demo-proxy-mesh-consumer
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "false"
    spec:
      containers:
        - name: server
          image: xxx
          imagePullPolicy: Never
          env:
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace

提供者部署两套Deployment,区别在于版本号不同(实际环境中是业务代码镜像不一样,这里简单意思一下)。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dubbo-demo-proxy-mesh-provider-v1 #v2
  namespace: dubbo-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dubbo-demo-proxy-mesh-provider
      version: v1 # v2
  template:
    metadata:
      labels:
        app: dubbo-demo-proxy-mesh-provider
        version: v1 # v2
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "false"
    spec:
      containers:
        - name: server
          image: xxx
          imagePullPolicy: Never
          env:
            - name: BIZ_VERSION
              value: v1 #v2

提供者Service,暴露50052端口,这个和k8s注册发现不同,这里的port必须准确。

apiVersion: v1
kind: Service
metadata:
  name: dubbo-demo-proxy-mesh-provider
  namespace: dubbo-demo
spec:
  type: ClusterIP
  selector:
    app: dubbo-demo-proxy-mesh-provider
  ports:
    - name: triple
      protocol: TCP
      port: 50052
      targetPort: 50052

istio相关资源,VirtualService和DestinationRule。

对于发往provider service的80端口http请求,转发到provider service的50052端口。

其中v1的权重是80,v2的权重是20。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: dubbo-demo-proxy-mesh-provider
  namespace: dubbo-demo
spec:
  hosts:
    - dubbo-demo-proxy-mesh-provider.dubbo-demo.svc.cluster.local
  http:
    - match:
        - port: 80
    - route:
        - destination:
            host: dubbo-demo-proxy-mesh-provider.dubbo-demo.svc.cluster.local
            subset: v1
            port:
              number: 50052
          weight: 80
        - destination:
            host: dubbo-demo-proxy-mesh-provider.dubbo-demo.svc.cluster.local
            subset: v2
            port:
              number: 50052
          weight: 20
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: dubbo-demo-proxy-mesh-provider
  namespace: dubbo-demo
spec:
  host: dubbo-demo-proxy-mesh-provider.dubbo-demo.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

请求消费者invoke端点正常输出。

$ kubectl exec -n dubbo-demo "$(kubectl get pod -l app=dubbo-demo-proxy-mesh-consumer -n dubbo-demo -o jsonpath='{.items[0].metadata.name}')" -- curl -s localhost:8080/invoke
v1, Hello Proxy Mesh, response from provider: 10.1.2.191:50052

消费者

SidecarMesh模式下,主要的区别在ReferenceConfig#createProxy

1)根据mesh相关参数,构建直连url;

2)利用直连url创建TripleInvoker;

3)将TripleInvoker转换为rpc服务代理;

Dubbo3源码(五)服务网格

本质上,SidecarMesh模式的消费者的逻辑,和直连提供者特性几乎一致

直连提供者的特点,在于用户手动指定ReferenceConfig的url属性。

而SidecarMesh模式消费者是框架负责构造这个ReferenceConfig的url属性。

构建MeshUrl

ReferenceConfig#meshModeHandleUrl:在SidecarMesh模式下构建直连url。

meshEnable开启的情况下,且用户没有指定直连url,进入下一步。

Dubbo3源码(五)服务网格

通过各种数据来源,拼接直连url。

  • podNamespace:优先取用户代码指定reference的providerNamespace,其次走环境变量POD_NAMESPACE,默认default
  • providedBy:相较于上一章k8s注册发现,SidecarMesh只能设置reference的providedBy来将rpc服务关联到应用;
  • clusterDomain:取环境变量CLUSTER_DOMAIN,默认cluster.local
  • meshPort:对端port,支持Envoy代理改目标port(比如案例中80转50052),默认80,可以通过设置reference.providerPort来修改;

最终url的拼接规则是:tri://{providedBy}.{podNamespace}.svc.{clusterDomain}:{meshPort}。

难道SidecarMesh模式下仅支持triple(gRPC)协议吗

Dubbo3源码(五)服务网格

构建Invoker

传统注册中心模式

在传统有注册中心的情况下,ReferenceConfig#aggregateUrlFromRegistry这个方法会将原始rpc协议替换为registryservice-discovery-registry

Dubbo3源码(五)服务网格

在之前【升级到应用注册发现】这一章提到过:

  • 如果启用2.7.5的应用注册发现,会走service-discovery-registry协议,RegistryProtocol;
  • 如果启用3.x的应用注册发现,会走registry协议,InterfaceCompatibleRegistryProtocol;

无论走哪个协议,最终rpc协议引用会在注册协议中完成,rpc协议引用的invokers会注入DynamicDirectory,被ClusterInvoker封装返回。

直连提供者模式

而在直连提供者模式下,url的协议不会被更改。

ReferenceConfig#parseUrl

1)支持分号分割;2)需要合并直连url参数和reference参数;

Dubbo3源码(五)服务网格

所以对于直连url,走自适应Protocol#refer,tri协议不会走注册逻辑,直接走TripleProtocol#refer返回TripleInvoker。(忽略包装Protocol)

MeshUrl模式

虽然SidecarMesh和直连提供者底层公用一个ReferenceConfig#url属性。

但是SidecarMesh只会存在一个url,因为构造MeshUrl时,只会存在一个tri协议的url,不会有什么分号分割。

针对SidecarMesh模式,其实并不需要Cluster层提供的路由、集群容错、负载均衡,这些事情都会交给sidecar(Envoy)来处理。

ReferenceConfig#createInvoker

所以可以不需要用Cluster#join来包装,直接返回TripleInvoker,而不是ClusterInvoker。

Dubbo3源码(五)服务网格

用户可以设置reference的unloadClusterRelated为true,来去除Cluster层。

但是默认这个属性为false,所以默认在SidecarMesh模式下,dubbo框架还是有Cluster层。

需要注意的是。

虽然底层就Directory中只有一个TripleInvoker,在默认的路由和负载均衡上不会有什么影响。

但是在默认的集群容错上,FailoverClusterInvoker还是会做3次重试,并不是完全委托给sidecar来处理。

提供者

其实看完消费者原理后,提供者原理更加简单。

ServiceConfig#exportRemote

在暴露rpc服务阶段,由于生产者未配置注册中心,所以直接走rpc协议暴露。

Dubbo3源码(五)服务网格

对于SidecarMesh情况,这里直接走自适应Protocol,根据rpc协议tri,直接走TripleProtocol,开启NettyServer。Dubbo3源码(五)服务网格

Proxyless Mesh(xDS)

案例

Proxyless模式依赖dubbo-xds模块提供注册发现功能。

业务代码与Sidecar模式完全一致。

提供者application.properties:

  • metadataServicePort:暴露元数据服务的端口为20885,这个配置项就是为了Proxyless模式提供的;
  • registry:选择xds协议,地址是istiod的service域名,端口是istiod的xds通讯端口;
spring.application.name=dubbo-demo-proxyless-mesh-provider
dubbo.application.name=${spring.application.name}
dubbo.application.metadataServicePort=20885
dubbo.registry.address=xds://istiod.istio-system.svc:15012
dubbo.protocol.name=tri
dubbo.protocol.port=50051

消费者application.properties:和提供者一致

spring.application.name=dubbo-demo-proxyless-mesh-consumer
dubbo.application.name=${spring.application.name}
dubbo.application.metadataServicePort=20885
dubbo.registry.address=xds://istiod.istio-system.svc:15012
dubbo.consumer.check=false

在部署上,一方面取消namespace=dubbo-demo默认注入istioproxy的标签。

kubectl label ns dubbo-demo istio-injection-

另一方面,下面不会使用istio的VirtualService等高级功能,因为目前我试了一下dubbo还未完全实现好。

简单暴露provider的service即可。

apiVersion: v1
kind: Service
metadata:
  name: dubbo-demo-proxyless-mesh-provider
  namespace: dubbo-demo
spec:
  clusterIP: None
  selector:
    app: dubbo-demo-proxyless-mesh-provider
  ports:
    - name: grpc
      protocol: TCP
      port: 50051
      targetPort: 50051

两类元数据

rpc服务元数据(MetadataInfo)

之前在2.7中介绍了应用级别注册发现,说到了元数据。

这类元数据都是rpc服务级元数据,可以理解为所有接口定义。

Dubbo3源码(五)服务网格

每个应用,对应一个MetadataInfo,即DefaultServiceInstance#serviceMetadata。

Dubbo3源码(五)服务网格

每个ServiceConfig暴露rpc服务,都会将对应接口定义发布到MetadataInfo。

AbstractServiceDiscovery#register(URL) :rpc服务定义注册到本地MetadataInfo。

Dubbo3源码(五)服务网格

这类接口元数据,默认使用metadataType= local模式使用。

启动阶段,provider会暴露MetadataService,consumer远程调用provider的MetadataService,获取MetadataInfo,组装providerUrl适配到rpc服务级别注册发现。

应用元数据(Map)

DefaultServiceInstance#metadata:

除了rpc服务元数据,还有一个应用元数据,往往与应用实例一起放到注册中心管理。

Dubbo3源码(五)服务网格

比如k8s注册中心,应用元数据被放入pod的annotation中,其中就包含了获取rpc服务元数据的关键参数,比如revisiondubbo.metadata-service.url-params

Dubbo3源码(五)服务网格

consumer需要先从注册中心拿到ServiceInstance#metadata,才能调用provider的MetadataService获取MetadataInfo,才能组装providerUrl适配到rpc服务级别注册发现。

提供者

回顾k8s注册

Proxyless有提供注册中心协议xds,整体流程和上一章k8s注册中心一致。

rpc服务和应用的映射关系,仍然需要consumer自己指定,provider不会与元数据中心通讯。

Dubbo3源码(五)服务网格

区别在于最后一步ServiceDiscovery的实现。

xds只是一个接口规范,与k8s不直接相关,不会修改pod注解来发布应用元数据。

Dubbo3源码(五)服务网格

发布应用元数据

XdsServiceDiscovery继承ReflectionBasedServiceDiscovery,注册逻辑都在父类里。

Dubbo3源码(五)服务网格

ReflectionBasedServiceDiscovery#updateInstanceMetadata

1)将应用元数据放入本地MetadataService管理;

2)通知所有监听当前应用元数据的consumer;(这个后面再看)

Dubbo3源码(五)服务网格

MetadataServiceDelegation#instanceMetadata

注意到在xds注册协议下,应用元数据并没有发布到任何外部系统,仅仅将这个数据存储到本地MetadataService中,这意味着消费者需要调用MetadataService来拿应用元数据

k8s注册,放在pod的注解中;zk注册,直接放在znode上。

Dubbo3源码(五)服务网格

暴露元数据服务

回到发布应用元数据的上一步。

ConfigurableMetadataServiceExporter#export:

暴露元数据服务,这其实是一段通用逻辑,不区分注册中心。

Dubbo3源码(五)服务网格

这里需要关注两个点:

  • MetadataService暴露协议和端口怎么获取(为什么consumer需要配置元数据服务协议和端口)
  • MetadataService暴露给consumer获取应用元数据的方法(consumer如何获取应用元数据)

协议和端口

InternalServiceConfigBuilder#protocol:元数据服务暴露协议

  • 优先,走应用级别设置的metadataServiceProtocol
  • 其次,走应用级别设置的metadata-service-protocol参数
  • 其次,根据protocol依次按照consumer/provider/protocol/application获取
  • 兜底,返回dubbo协议

Dubbo3源码(五)服务网格

InternalServiceConfigBuilder#port:元数据服务暴露端口

  • 优先,走应用级别设置的metadataServicePort
  • 其次,走应用级别设置的metadata-service-port参数
  • 兜底,返回Protocol对应port

Dubbo3源码(五)服务网格

如何获取应用元数据

ConfigurableMetadataServiceExporter#generateMethodConfig

框架暴露了一个MetadataService的callback方法,这个特性见官网服务端对客户端进行回调

Dubbo3源码(五)服务网格

MetadataService#getAndListenInstanceMetadata

这是consumer获取provider的应用元数据的callback接口定义。

Dubbo3源码(五)服务网格

MetadataServiceDelegation#getAndListenInstanceMetadata:provider对于获取应用元数据的实现

  • 将Listener注册到本地map,当应用元数据变更时,回调consumer;
  • 返回MetadataServiceDelegation.instanceMetadata内存中的应用元数据;

Dubbo3源码(五)服务网格

消费者

回顾服务引用

继续回顾一下服务引用流程:

1)Registry#subscribe:订阅服务并获取服务提供者urls,通知Directory;

2)RegistryDirectory#notifyDirectory:收到服务提供者urls,委派RpcProtocol#refer构造底层通讯invokers,如TripleInvoker;

3)Cluster#join,封装Directory为ClusterInvoker;

4)ProxyFactory#getProxy:创建rpc服务代理;

重点回顾一下第一步,即ServiceDiscoveryRegistry#subscribe,应用级别订阅:

Dubbo3源码(五)服务网格

1)serviceDiscovery#subscribe:将订阅url保存到当前应用的MetadataInfo中

2)获取rpc服务->应用名称列表:和k8s一致,目前需要指定reference的providedby或注册中心的subscribed-services

3)subscribeUrls(特殊点) :根据应用名查询并订阅应用实例ServiceInstance,构造rpc服务提供者urls,通知Directory

ServiceDiscoveryRegistry#subscribeURLs:这是proxyless模式下比较特殊的两点,即查询和订阅ServiceInstance变更

1)ServiceDiscovery#getInstance:查询应用下所有ServiceInstance,通知Directory;

2)ServiceDiscovery#addServiceInstancesChangedListener:订阅ServiceInstance变更;Dubbo3源码(五)服务网格

xDS铺垫

xDS是Envoy提供的一套api规范,用于控制面(Istio)与数据面(Envoy)通讯,提供各种资源发现能力,如后面会遇到的三种:

  • LDS:Listener Discovery Service,发现Listener
  • RDS:Route Discovery Service,发现RouteConfiguration
  • EDS:Endpoint Discovery Service,发现Endpoint,这是消费者最关心的数据,每个Endpoint代表一个ServiceInstance

在Proxyless模式下,Dubbo实现了xDS协议,可以与Istio通讯,Dubbo自己就是Envoy。

无论获取哪种资源,出入参都是一致:

DiscoveryRequest:获取资源入参,这里只重点关注几个业务参数

  • node:当前客户端节点的唯一标识;
  • type_url:标识是哪种资源,比如LDS是type.googleapis.com/envoy.config.listener.v3.Listener;
  • resource_names:资源名称列表;
{
  "version_info": ...,
  "node": {...},
  "resource_names": [],
  "type_url": ...,
  "response_nonce": ...,
  "error_detail": {...}
}

DiscoveryResponse:获取资源出参

  • type_url:标识是哪种资源;
  • resources:资源列表;
{
  "version_info": ...,
  "resources": [],
  "type_url": ...,
  "nonce": ...,
  "control_plane": {...}
}

xDS同时支持REST-JSON和gRPC双向流(参考之前Triple双向流是一样的),dubbo用的是后者。

基于gRPC双向流,还区分出两种方式:

  • 传统方式,不同资源,不同请求路径,不同流
  • ADS,dubbo选择这种,所有资源,同一个请求路径,同一个流

LDS/RDS订阅

XdsServiceDiscovery创建阶段,即ServiceDiscoveryRegistry构建阶段,会利用注册URL,执行LDS和RDS订阅Listener和Route。

Dubbo3源码(五)服务网格

PilotExchanger

1)LDS:查询Listener,缓存在PilotExchanger.listenerResult;

2)RDS:查询RouteConfiguration,缓存在PilotExchanger.routeResult;

3)RDS:订阅RouteConfiguration

4)LDS:订阅Listener

Dubbo3源码(五)服务网格

LDS

LDS会订阅所有Listnener。

LDS解析结果ListenerResult仅保留调用RDS需要用到的入参,即RDS资源名。

Dubbo3源码(五)服务网格

比如本次案例,routeConfigName=50051,可用于查询RDS。

Dubbo3源码(五)服务网格

LdsProtocol#decodeDiscoveryResponse:解析Listener数据报文,仅返回Filter链中类型为HttpConnectionManagerRds.RouteConfigName,用于调用RDS发现路由配置。

Dubbo3源码(五)服务网格

RDS

RDS的解析结果RouteResult分为两部分:

  • domainMapkey是k8s中的域名,value是查询EDS要用的cluster,在dubbo中用于服务发现。这个map是解析VirtualHost路由表后得到的全量cluster,并未做任何路由匹配逻辑;
  • virtualHostMapkey是k8s中的域名,value是原始Envoy定义的VirtualHost路由表,在dubbo中用于xds路由(XdsRouter)

xds路由部分本章不做介绍,因为研究了一下,bug比较多,不建议使用。

可以简单认为,目前dubbo-xds仅支持服务注册和发现,不支持envoy更多高级功能。

Dubbo3源码(五)服务网格

比如下面是Rds.RouteConfigName=50051的原始RDS报文。

dubbo-demo-proxyless-mesh-provider域名下,cluster仅包含outbound|50051||dubbo-demo-proxyless-mesh-provider.dubbo-demo.svc.cluster.local。

Dubbo3源码(五)服务网格

RdsProtocol# decodeDiscoveryResponse

解析RouteConfiguration,可以看到domainMap的来源就是路由表,将VirtualHost下的所有域名和routes.route.cluster做了一个join,变成了宽表。

Dubbo3源码(五)服务网格

查询Endpoint

XdsServiceDiscovery#getInstances:在初始构造Directory时,主动查询应用下所有Endpoint,构造ServiceInstance集合。

Dubbo3源码(五)服务网格

PilotExchanger#getEndpoints

1)根据传入应用名domain,查询RouteResult中的domainMap,定位cluster,比如:domain=dubbo-demo-proxyless-mesh-provider,定位到cluster=outbound|50051||dubbo-demo-proxyless-mesh-provider.dubbo-demo.svc.cluster.local;

2)调用EdsProtocol根据cluster查询endpoint;

Dubbo3源码(五)服务网格

EDS根据cluster查询endpoint原始报文如下:

Dubbo3源码(五)服务网格

EdsProtocol#decodeResourceToEndpoint:解析返回Endpoint资源。

Dubbo3源码(五)服务网格

EDS订阅

XdsServiceDiscovery#addServiceInstancesChangedListener

调用PilotExchanger订阅Endpoint。

Dubbo3源码(五)服务网格

PilotExchanger#observeEndpoints

1)注册业务监听;2)发送订阅eds请求

Dubbo3源码(五)服务网格

PilotExchanger#doObserveEndpoints

和查询Endpoint一致,从缓存RDS的RouteResult中找到domain下所有cluster,调用eds。

当endpoint变更,回调业务监听,即ReflectionBasedServiceDiscovery#notifyListener。

Dubbo3源码(五)服务网格

ReflectionBasedServiceDiscovery#notifyListener

当Endpoint变更,收到endpoint变更回调。

md5校验instance列表确实发生变更。

通知ServiceInstancesChangedListener构造providerUrl(通知Directory构造更新invokers)。

Dubbo3源码(五)服务网格

构造ServiceInstance

无论是初始查询Endpoint还是订阅Endpoint发生变更,都需要组合application和Endpoint信息为ServiceInstance通知Directory。

XdsServiceDiscovery#changedToInstances

1)利用应用名+xds拿到的Endpoint构造基础的ServiceInstance;

2)ReflectionBasedServiceDiscovery#fillServiceInstance:填充应用元数据(revision等);

和普通注册中心比较,目前xds注册无法从注册中心拿到应用元数据,需要循环所有provider实例,主动获取应用元数据。

和rpc服务元数据比较,rpc服务元数据是根据应用元数据中的revision,每个revision只需要获取一次,大部分情况下,同一个应用所有实例revision相同,只需要调用一次。

Dubbo3源码(五)服务网格

ReflectionBasedServiceDiscovery#fillServiceInstance:填充应用元数据

1)ReflectionBasedServiceDiscovery#getMetadataServiceProxy:引用元数据服务;

2)MetadataService#getAndListenInstanceMetadata:调用provider监听应用元数据,缓存在本地,如果对端返回空串,代表对端取消注册下线;

Dubbo3源码(五)服务网格

普通注册中心,需要拿到ServiceInstance的应用元数据(比如zk从znode上拿,k8s从pod注册上拿),知道对端暴露的元数据服务端口,才能引用元数据服务。

xds注册,要先引用元数据服务,再拿应用元数据填充ServiceInstance。

MetadataUtils#referProxy:关注StandardMetadataServiceURLBuilder如何构造元数据服务url。

Dubbo3源码(五)服务网格

StandardMetadataServiceURLBuilder#build

对于普通注册中心,可以从ServiceInstance上直接拿到dubbo.metadata-service.url-params参数,就可以拿到引用MetadataService的必要参数。

Dubbo3源码(五)服务网格

StandardMetadataServiceURLBuilder#generateUrlWithoutMetadata

1)元数据服务端口:优先取consumer.metadataServicePort(本例中是20885),其次取ServiceInstance的port(即eds返回的endpoint的port,即k8s的service的port,本例中是50051);

2)元数据服务rpc协议:写死dubbo;

3)监听元数据方法配置:getAndListenInstanceMetadata.1.callback=true;

Dubbo3源码(五)服务网格

总结

Sidecar(Proxy) Mesh

使用特点

注册中心为空;

dubbo.consumer.meshEnable设置为true;

reference只能通过设置providedBy来指定rpc服务提供者应用名(k8s还能通过设置注册中心参数subscribed-services)。

服务注册

对于服务注册来说,由于注册协议为空,底层会直接开启底层通讯Server,不会走RegistryProtocol逻辑。

服务发现

对于服务发现来说,Sidecar Mesh本质上利用了直连提供者的特性。

只不过直连url由dubbo框架通过参数拼接来完成:tri://{providedBy}.{podNamespace}.svc.{clusterDomain}:{meshPort},协议固定为triple。其实理论上来说,如果确定底层是直连提供者逻辑,用户可以手动指定reference的直连url来启用其他协议,只要sidecar能支持。

此外,对于消费者来说,所有容错、路由等逻辑应该交给envoy sidecar来管理。

但是目前默认情况下,虽然底层只有一个直连url对应的TripleInvoker,Dubbo Cluster层依然存在。

所以FailoverClusterInvoker还是会做3次重试,并不是完全委托给sidecar来处理。

可以设置reference的unloadClusterRelated为true,来去除Cluster层。

Proxyless Mesh

使用特点

注册中心:xds协议,地址是istiod的service域名,端口是istiod的xds通讯端口;

元数据服务端口:需要手动指定元数据服务暴露端口metadataServicePort;

元数据

  • 应用元数据:包含rpc服务元数据revision、元数据服务相关信息等;
  • rpc服务元数据:传统意义上的元数据,即MetadataInfo,包含当前应用实例暴露的所有rpc服务信息。默认local模式存储在本地,provider暴露MetadataService服务,运行时consumer需要根据应用元数据中的MetadataService信息(协议、端口),调用provider获取;支持remote存储到元数据中心。

目前的Proxyless模式,应用元数据没有找到地方集中存放。

需要通过provider暴露MetadataService服务供consumer获取,变成了鸡生蛋和蛋生鸡的问题。

所以provider和consumer需要以约定的方式,暴露和引用元数据服务:

  • 元数据服务协议:写死dubbo;
  • 元数据服务端口:metadataServicePort指定;

consumer需要循环调用每个provider实例来获取应用元数据,这区别于rpc服务应用元数据可以通过revision减少调用次数。

对于应用元数据的动态更新,dubbo采用了服务端对客户端进行回调这个特性实现,这也是为什么协议只能是dubbo吧。(CallbackServiceCodec属于dubbo协议)

服务注册

区别于其他注册中心,Proxyless模式下,provider只需要将应用元数据缓存到本地,即可完成注册。

服务发现

在启动阶段,消费者需要通过xDS与istio通讯,来获取并监听路由表。

首先LDS会发现Listener下所有路由名,比如本例中路由名是50051。

Dubbo3源码(五)服务网格

接着RDS通过路由名获取所有路由配置,即应用名和cluster的映射关系。

比如本例中是dubbo-demo-proxyless-mesh-provider下只有outbound|50051||dubbo-demo-proxyless-mesh-provider.dubbo-demo.svc.cluster.local。

Dubbo3源码(五)服务网格

当consumer需要订阅某个应用时,需要从路由表中找到cluster,调用EDS获取cluster下所有Endpoint,得到pod的ip和Service端口。

Dubbo3源码(五)服务网格

当Listener变更(LDS)重新查Route(RDS);

当Route变更(RDS)重新查Endpoint(EDS);

当Endpoint变更通知XdsServiceDiscovery。

目前dubbo-xds对于单纯的服务注册发现没什么问题,但是XdsRouter对于高级的路由功能支持并不是很好。

参考文档

欢迎大家评论或私信讨论问题。

本文原创,未经许可不得转载。

欢迎关注公众号【程序猿阿越】。

转载自:https://juejin.cn/post/7241596534613016633
评论
请登录