Dubbo3源码(五)服务网格
前言
本章基于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服务代理;
本质上,SidecarMesh模式的消费者的逻辑,和直连提供者特性几乎一致。
直连提供者的特点,在于用户手动指定ReferenceConfig的url属性。
而SidecarMesh模式消费者是框架负责构造这个ReferenceConfig的url属性。
构建MeshUrl
ReferenceConfig#meshModeHandleUrl:在SidecarMesh模式下构建直连url。
当meshEnable开启的情况下,且用户没有指定直连url,进入下一步。
通过各种数据来源,拼接直连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)协议吗?
构建Invoker
传统注册中心模式
在传统有注册中心的情况下,ReferenceConfig#aggregateUrlFromRegistry这个方法会将原始rpc协议替换为registry或service-discovery-registry。
在之前【升级到应用注册发现】这一章提到过:
- 如果启用2.7.5的应用注册发现,会走service-discovery-registry协议,RegistryProtocol;
- 如果启用3.x的应用注册发现,会走registry协议,InterfaceCompatibleRegistryProtocol;
无论走哪个协议,最终rpc协议引用会在注册协议中完成,rpc协议引用的invokers会注入DynamicDirectory,被ClusterInvoker封装返回。
直连提供者模式
而在直连提供者模式下,url的协议不会被更改。
ReferenceConfig#parseUrl:
1)支持分号分割;2)需要合并直连url参数和reference参数;
所以对于直连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。
用户可以设置reference的unloadClusterRelated为true,来去除Cluster层。
但是默认这个属性为false,所以默认在SidecarMesh模式下,dubbo框架还是有Cluster层。
需要注意的是。
虽然底层就Directory中只有一个TripleInvoker,在默认的路由和负载均衡上不会有什么影响。
但是在默认的集群容错上,FailoverClusterInvoker还是会做3次重试,并不是完全委托给sidecar来处理。
提供者
其实看完消费者原理后,提供者原理更加简单。
ServiceConfig#exportRemote:
在暴露rpc服务阶段,由于生产者未配置注册中心,所以直接走rpc协议暴露。
对于SidecarMesh情况,这里直接走自适应Protocol,根据rpc协议tri,直接走TripleProtocol,开启NettyServer。
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服务级元数据,可以理解为所有接口定义。
每个应用,对应一个MetadataInfo,即DefaultServiceInstance#serviceMetadata。
每个ServiceConfig暴露rpc服务,都会将对应接口定义发布到MetadataInfo。
AbstractServiceDiscovery#register(URL) :rpc服务定义注册到本地MetadataInfo。
这类接口元数据,默认使用metadataType= local模式使用。
启动阶段,provider会暴露MetadataService,consumer远程调用provider的MetadataService,获取MetadataInfo,组装providerUrl适配到rpc服务级别注册发现。
应用元数据(Map)
DefaultServiceInstance#metadata:
除了rpc服务元数据,还有一个应用元数据,往往与应用实例一起放到注册中心管理。
比如k8s注册中心,应用元数据被放入pod的annotation中,其中就包含了获取rpc服务元数据的关键参数,比如revision和dubbo.metadata-service.url-params。
consumer需要先从注册中心拿到ServiceInstance#metadata,才能调用provider的MetadataService获取MetadataInfo,才能组装providerUrl适配到rpc服务级别注册发现。
提供者
回顾k8s注册
Proxyless有提供注册中心协议xds,整体流程和上一章k8s注册中心一致。
rpc服务和应用的映射关系,仍然需要consumer自己指定,provider不会与元数据中心通讯。
区别在于最后一步ServiceDiscovery的实现。
xds只是一个接口规范,与k8s不直接相关,不会修改pod注解来发布应用元数据。
发布应用元数据
XdsServiceDiscovery继承ReflectionBasedServiceDiscovery,注册逻辑都在父类里。
ReflectionBasedServiceDiscovery#updateInstanceMetadata:
1)将应用元数据放入本地MetadataService管理;
2)通知所有监听当前应用元数据的consumer;(这个后面再看)
MetadataServiceDelegation#instanceMetadata:
注意到在xds注册协议下,应用元数据并没有发布到任何外部系统,仅仅将这个数据存储到本地MetadataService中,这意味着消费者需要调用MetadataService来拿应用元数据。
k8s注册,放在pod的注解中;zk注册,直接放在znode上。
暴露元数据服务
回到发布应用元数据的上一步。
ConfigurableMetadataServiceExporter#export:
暴露元数据服务,这其实是一段通用逻辑,不区分注册中心。
这里需要关注两个点:
- MetadataService暴露协议和端口怎么获取(为什么consumer需要配置元数据服务协议和端口)
- MetadataService暴露给consumer获取应用元数据的方法(consumer如何获取应用元数据)
协议和端口
InternalServiceConfigBuilder#protocol:元数据服务暴露协议
- 优先,走应用级别设置的metadataServiceProtocol
- 其次,走应用级别设置的metadata-service-protocol参数
- 其次,根据protocol依次按照consumer/provider/protocol/application获取
- 兜底,返回dubbo协议
InternalServiceConfigBuilder#port:元数据服务暴露端口
- 优先,走应用级别设置的metadataServicePort
- 其次,走应用级别设置的metadata-service-port参数
- 兜底,返回Protocol对应port
如何获取应用元数据
ConfigurableMetadataServiceExporter#generateMethodConfig:
框架暴露了一个MetadataService的callback方法,这个特性见官网服务端对客户端进行回调。
MetadataService#getAndListenInstanceMetadata:
这是consumer获取provider的应用元数据的callback接口定义。
MetadataServiceDelegation#getAndListenInstanceMetadata:provider对于获取应用元数据的实现
- 将Listener注册到本地map,当应用元数据变更时,回调consumer;
- 返回MetadataServiceDelegation.instanceMetadata内存中的应用元数据;
消费者
回顾服务引用
继续回顾一下服务引用流程:
1)Registry#subscribe:订阅服务并获取服务提供者urls,通知Directory;
2)RegistryDirectory#notifyDirectory:收到服务提供者urls,委派RpcProtocol#refer构造底层通讯invokers,如TripleInvoker;
3)Cluster#join,封装Directory为ClusterInvoker;
4)ProxyFactory#getProxy:创建rpc服务代理;
重点回顾一下第一步,即ServiceDiscoveryRegistry#subscribe,应用级别订阅:
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变更;
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和DiscoveryResponse,数据面传入所有关心的资源名称,控制面返回所有资源列表,目前dubbo使用这种方式;
- 增量:DeltaDiscoveryRequest和DeltaDiscoveryRequest,数据面仅传入需要增量订阅或取消订阅的资源;
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。
PilotExchanger:
1)LDS:查询Listener,缓存在PilotExchanger.listenerResult;
2)RDS:查询RouteConfiguration,缓存在PilotExchanger.routeResult;
3)RDS:订阅RouteConfiguration;
4)LDS:订阅Listener;
LDS
LDS会订阅所有Listnener。
LDS解析结果ListenerResult仅保留调用RDS需要用到的入参,即RDS资源名。
比如本次案例,routeConfigName=50051,可用于查询RDS。
LdsProtocol#decodeDiscoveryResponse:解析Listener数据报文,仅返回Filter链中类型为HttpConnectionManager的Rds.RouteConfigName,用于调用RDS发现路由配置。
RDS
RDS的解析结果RouteResult分为两部分:
- domainMap:key是k8s中的域名,value是查询EDS要用的cluster,在dubbo中用于服务发现。这个map是解析VirtualHost路由表后得到的全量cluster,并未做任何路由匹配逻辑;
- virtualHostMap:key是k8s中的域名,value是原始Envoy定义的VirtualHost路由表,在dubbo中用于xds路由(XdsRouter)
xds路由部分本章不做介绍,因为研究了一下,bug比较多,不建议使用。
可以简单认为,目前dubbo-xds仅支持服务注册和发现,不支持envoy更多高级功能。
比如下面是Rds.RouteConfigName=50051的原始RDS报文。
dubbo-demo-proxyless-mesh-provider域名下,cluster仅包含outbound|50051||dubbo-demo-proxyless-mesh-provider.dubbo-demo.svc.cluster.local。
RdsProtocol# decodeDiscoveryResponse:
解析RouteConfiguration,可以看到domainMap的来源就是路由表,将VirtualHost下的所有域名和routes.route.cluster做了一个join,变成了宽表。
查询Endpoint
XdsServiceDiscovery#getInstances:在初始构造Directory时,主动查询应用下所有Endpoint,构造ServiceInstance集合。
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;
EDS根据cluster查询endpoint原始报文如下:
EdsProtocol#decodeResourceToEndpoint:解析返回Endpoint资源。
EDS订阅
XdsServiceDiscovery#addServiceInstancesChangedListener:
调用PilotExchanger订阅Endpoint。
PilotExchanger#observeEndpoints:
1)注册业务监听;2)发送订阅eds请求
PilotExchanger#doObserveEndpoints:
和查询Endpoint一致,从缓存RDS的RouteResult中找到domain下所有cluster,调用eds。
当endpoint变更,回调业务监听,即ReflectionBasedServiceDiscovery#notifyListener。
ReflectionBasedServiceDiscovery#notifyListener:
当Endpoint变更,收到endpoint变更回调。
md5校验instance列表确实发生变更。
通知ServiceInstancesChangedListener构造providerUrl(通知Directory构造更新invokers)。
构造ServiceInstance
无论是初始查询Endpoint还是订阅Endpoint发生变更,都需要组合application和Endpoint信息为ServiceInstance通知Directory。
XdsServiceDiscovery#changedToInstances:
1)利用应用名+xds拿到的Endpoint构造基础的ServiceInstance;
2)ReflectionBasedServiceDiscovery#fillServiceInstance:填充应用元数据(revision等);
和普通注册中心比较,目前xds注册无法从注册中心拿到应用元数据,需要循环所有provider实例,主动获取应用元数据。
和rpc服务元数据比较,rpc服务元数据是根据应用元数据中的revision,每个revision只需要获取一次,大部分情况下,同一个应用所有实例revision相同,只需要调用一次。
ReflectionBasedServiceDiscovery#fillServiceInstance:填充应用元数据
1)ReflectionBasedServiceDiscovery#getMetadataServiceProxy:引用元数据服务;
2)MetadataService#getAndListenInstanceMetadata:调用provider监听应用元数据,缓存在本地,如果对端返回空串,代表对端取消注册下线;
普通注册中心,需要拿到ServiceInstance的应用元数据(比如zk从znode上拿,k8s从pod注册上拿),知道对端暴露的元数据服务端口,才能引用元数据服务。
xds注册,要先引用元数据服务,再拿应用元数据填充ServiceInstance。
MetadataUtils#referProxy:关注StandardMetadataServiceURLBuilder如何构造元数据服务url。
StandardMetadataServiceURLBuilder#build:
对于普通注册中心,可以从ServiceInstance上直接拿到dubbo.metadata-service.url-params参数,就可以拿到引用MetadataService的必要参数。
StandardMetadataServiceURLBuilder#generateUrlWithoutMetadata:
1)元数据服务端口:优先取consumer.metadataServicePort(本例中是20885),其次取ServiceInstance的port(即eds返回的endpoint的port,即k8s的service的port,本例中是50051);
2)元数据服务rpc协议:写死dubbo;
3)监听元数据方法配置:getAndListenInstanceMetadata.1.callback=true;
总结
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。
接着RDS通过路由名获取所有路由配置,即应用名和cluster的映射关系。
比如本例中是dubbo-demo-proxyless-mesh-provider下只有outbound|50051||dubbo-demo-proxyless-mesh-provider.dubbo-demo.svc.cluster.local。
当consumer需要订阅某个应用时,需要从路由表中找到cluster,调用EDS获取cluster下所有Endpoint,得到pod的ip和Service端口。
当Listener变更(LDS)重新查Route(RDS);
当Route变更(RDS)重新查Endpoint(EDS);
当Endpoint变更通知XdsServiceDiscovery。
目前dubbo-xds对于单纯的服务注册发现没什么问题,但是XdsRouter对于高级的路由功能支持并不是很好。
参考文档
- Dubbo Service Mesh:cn.dubbo.apache.org/zh-cn/overv…
- xDS protocol:www.envoyproxy.io/docs/envoy/…
欢迎大家评论或私信讨论问题。
本文原创,未经许可不得转载。
欢迎关注公众号【程序猿阿越】。
转载自:https://juejin.cn/post/7241596534613016633