做者 | 曹胜利 Apache Dubbo PMCnode
导读:Dubbo 做为高性能 Java RPC 框架的刻板印象早已深刻人心,在 Cloud Native 的架构选型上,Spring Cloud 或许才是业界的优先选择。实际上,Dubbo 已经悄然地衍进为 Cloud Native 基础设施,不只承袭过去 RPC 时代的荣耀,并且也完善了现有基础设施的缺失。自从容器和 K8s 登上舞台以后,给原有的 RPC 领域带来了很大的挑战,本文主要讲述 RPC 领域遇到的问题,以及 RPC 怎么去拥抱 K8s 的一些思考。git
Kubernetes 是一个开源的,用于管理云平台中多个主机上的容器化的应用, Kubernetes 的目标是让部署容器化的应用简单而且高效, Kubernetes 提供了应用部署、规划、更新、维护的一种机制。Kubernetes 简称 K8s。github
在 Kubernetes 中,最小的管理元素不是一个个独立的容器,而是 Pod 。Pod 的生命周期须要注意如下几点:spring
若是一些 Pods 提供了一些功能供其它的 Pod 使用,在 Kubernete 集群中是如何实现让这些前台可以持续的追踪到这些后台的?答案是:Service 。编程
Kubernete Service 是一个定义了一组 Pod 的策略抽象,这些被服务标记的 Pod 通常都是经过 label Selector 决定的。Service 抽象了对 Pod 的访问。api
默认的 Service ,经过一个集群 IP 获取 A Record 。可是有时须要返回全部知足条件的 Pod IP 列表,这时候能够直接使用 Headless Services 。安全
参考:kubernetes.io/服务器
推荐书籍:kubernetes in action微信
随着微服务的普及,应用之间的通讯有了足够多的成熟方案。网络
从功能层面来讲,对开发者有感知的功能有:
从选型角度会关注如下几点:
关键流程:
关键知识点:
备注:Dubbo 和 HSF 的大部分机制都是类似的,因此下面都以 Dubbo 做为方案进行讨论。
Spring Cloud 经过 Rest 形式进行网络调用。应用开发者能够本身编写暴露 Rest 服务,如 springmvc 。
Spring Cloud 里的服务注册是应用维度( Eureka ),Client 端和 Server 端经过约定的方式进行通讯。
Spring Cloud 提供了一套标准 API ,而其中 Netflix 是其中的佼佼者,对这套 API 进行了实现,对大部分开发者来讲,能够回直接依赖和使用 Netflix ,因此能够说是 Netflix 提供成了 Spring Cloud 的核心。可是做为商业公司对开源投入每每会多变,如 Eureka 已经体制维护。
gRPC 是一个基于 HTTP/2 协议设计的 RPC 框架,它采用了 Protobuf 做为 IDL。gRPC 做为端到端的通讯方案,能够解决如今的多语言问题。
gRPC 自己不提供服务注册,服务治理的功能。但如今能够看到 gRpc 有趋势往这方面扩展的野心。
K8s 体系里暂时没有公允的通讯框架,通常推荐 gRPC 做为 RPC 框架。
K8s 体系的默认状况下, Pod 的 IP 是变化的,因此 Pod 和 Pod 之间须要通讯的话,有几种方式:
Istio 的控制层会向 K8s 的 Api server 请求并监听 pod 信息,service 信息等信息。这样 Istio 中就有了完整的 K8s 集群中的 pod,service 等的完整信息。若是 K8s 集群中有信息变动,Istio 中也能够获得通知并更新对应的信息。
Envoy 做为 Proxy 一个最多见的实现,以 Envoy 做为例子简单介绍。Envoy 经过查询文件或管理服务器来动态发现资源。对应的发现服务及其相应的 Api 被称做 xDS 。协议内容包括 LDS、RDS、CDS 等等。
参考资料:
Service Mesh 介绍:www.infoq.cn/article/pat…Istio
路由规则:istio.io/docs/tasks/…
备注:上述知识是经过查阅资料( Istio 官网),以及和集团 Service Mesh 同窗沟通得到。若有问题,欢迎指正。
Dubbo 默认是基于 TCP 通讯,Spring Cloud 大部分基于 Rest 请求。在阿里云实施商业化过程当中,发现大量公司须要 Spring Cloud 应用和 Dubbo 进行通讯,社区主要依靠 Dubbo 上增长一层网关来解决。
是否有方案进行统一服务注册发现,以及服务调用呢?
基础理论能够参考:
K8s 下 Pod 的 IP 是变化的 (默认),Dubbo 的服务治理高度依赖 IP 。
K8s 的服务注册经过 Pod 定义完成,服务发现实际上是寻找 Pod 的过程。Pod 和应用有必定的对应关系,和 Dubbo 里的接口维度的服务注册发现模型不是很匹配。
Dubbo 须要进行支持裁剪,Dubbo 的大部分功能均可以交由 sidecar ( proxy )来完成。
若是公司已经在部署 RPC 框架,这时候若是须要实施 Service Mesh ,有什么好的过渡方案吗?
服务怎么定义呢?须要从应用开发者角度看待怎么定义服务。
服务在功能维度对应某一功能,如查询已买订单详情。在 Dubbo 中,对应某个接口下的方法;在 Spring Cloud 和 gRPC 对应一个 http 请求。
若是从面向函数编程角度,一个服务就是一个 function 。在 Java 语言中,class 是一切编程的基础,因此将某些服务按照必定的维度进行聚合,放到某个接口中,就成了一个接口包含了不少的服务。
从 Dubbo 角度来解释下:Dubbo 是面向接口编程的远程通讯,因此 Dubbo 是面向服务集的编程,你若是想调用某个服务,必须经过接口的方式引入,而后调用接口中的某个服务。Dubbo Ops 中提供的服务查询功能,其实不是查询单个服务,而是经过查询接口(服务集)以后得到具体的方法(服务)。
而在 Spring Cloud 的世界里,服务提供方会将本身的应用信息( Ip+port )注册成应用下的一个实例,服务消费方和服务提供方按照约定的形式进行 Rest 请求。每一个请求对应的也是一个服务。
K8s 里的 Service 实际上是对应到一组 pod+port 列表,和 DNS 联系紧密;用通俗易懂的方式表达:维护了 pod 集合的关系映射。和上面讲的服务是属于不一样场景下的两个概念。
按照这个方式定义服务,服务治理的粒度其实也是按照服务粒度,能够针对每一个服务设置超时时间,设置路由规则等等。可是服务注册的粒度和服务有什么关系呢?
一个应用下包含了不少接口,一个接口下包含了不少服务( Dubbo );或者一个应用包含了不少的服务( Spring Cloud )。分析下应用维度注册和接口维度注册的优缺点。会有一篇独立的文章来阐述应用维度注册的方案。
优势:
缺点:
优势:
缺点:
目标:
Dubbo 改形成应用维度的服务注册。(具体不展开,后面文章说明)
Dubbo 实现中,支持将以 Rest 协议进行暴露,而且让 Spring Cloud 识别。
在 K8s 已经阐述过,下面的内容也是假设一个应用部署在一个容器里,一个容器部署在一个 pod 里。
接下来方案的讨论,互相之间实际上是有关联的,如服务治理可能会影响到服务注册发现,服务查询也不能依赖于服务注册的内容。整个设计的过程是不断优化的过程。下面所说的内容,以 Dubbo 来举例说明。
Dubbo 原有体系里的服务治理是强依赖于 IP ,当配置了一套服务治理规则的时候,最后都是基于一个或多个 IP 地址。
到 K8s 体系下以后,要考虑的是 Pod 的 IP 不是固定的。因此当前的路由规则不能知足条件,并且会产生不少规则垃圾数据。K8s 体系下,经过 service 查找 Pod ,是基于 label selector ;经过 deployment 管理 Pod ,其实也是基于 Pod label selector 。因此 pod label selector 是在 K8s 习题中比较通用的解决方案。
以路由规则为例,须要支持一种新的路由规则:label 路由。经过必定条件匹配以后,将结果定位到以 label selector 查询到的 Pod 列表里,而非原来的 ip 列表。
要支持 label 路由,client 端须要获取到 client 端本身的 Pod label 信息,还须要获取到 server pod 列表中每一个 Pod 的 label 信息。
Pod 定义环境变量,应用获取;Dubbo 提供对环境变量读取的支持,Pod 中须要按照 Dubbo 定义的环境变量设置具体的 pod 信息。
经过 Downward API 传递 Pod 信息;Dubbo 须要提供对 Downward 的目录读取,Pod 中须要定制 downward 的对应配置。
经过 API Server 获取数据;最强大的方式,可是应用须要强依赖于 API Server 。
经过调用其余 Pod 的服务获取;依赖于应用能获取自身的 Pod 信息,同时将自身的 Pod 信息暴露成服务( rest 或 dubbo 协议)。client 端经过调用对用的 Pod 获取到对应 Pod 的完整信息。
经过 Api server 获取数据;很强大,但增长了对 Api server 的依赖。
K8s 体系下,RPC 服务发现有如下几种方式:
注册机制:将 IP 写入注册中心,用心跳保持链接;小心跳中止,从注册中心删除;
利用 Service+DNS :新建一个 Service ,能够经过标签选择到一组 Pod 列表,这个 Service 对应一个不变的集群 IP ;Client 端经过 DNS 方式或者直接访问集群 IP 。这个集群 IP ,约等于实现了负载均衡 ( iptable 方式);
利用 headless service(DNS) :headless service 和上面的 service 的区别是,它不提供集群 IP ,经过主机名的形式获取一组 IP 列表,Client 端本身决定访问哪一个 Pod ;
api server :Client 端直接请求 api server ,获取到 pod 的列表, Client 本身决定访问 pod 的逻辑。同时获取的时候增长 watch ,api server 会将 pod 的变化信息同步 Client 。
经过拿到 Server 端的 IP 或者 host ,Client 端就能够发起 http 或者其余协议的请求。
下面介绍符合 Dubbo 的可行方案:
这是最简单的方式,Dubbo 自己不须要作任何改造。带来的问题是增长了 Zookeeper 的维护,同时这个方案很不云原生,和 K8s 的体系没有任何关系。
上面方案是将 ZooKeeper 做为注册中心,那么是否能够将 K8s 里 service 做为注册中心呢?Dubbo 里每一个接口去创建一个 service ,每一个应用实例启动过程当中去更新 Endpoint 信息,创建 Service-> Endpoint-> IP 列表的关系。
这种方案中 K8s service 的定义被改造了,并且定义了过多的 service ,service 的维护管理是个难题。
在传统的 RPC 领域,服务分红服务注册和服务发现。在 K8s 领域 pod 和应用是一对一的关系,K8s 自己就提供了 pod 查找的能力,因此必定程度上服务注册其实能够不存在,而只须要服务发现。可是这个其实须要一个前提:
Dubbo 须要将服务注册发现的粒度改形成应用维度。> 在运维层面,将 app=xxx (应用名)写入到 pod 的 label 中。
若是 K8s service 提供了cluster ip ,那么 Dubbo 只负责调用该集群 Ip ,路由和负载均衡的逻辑则交给了 K8s 的 proxy 来完成。此方案削减了 Dubbo 的核心能力。接下来讨论 headless service 提供的能力。
经过请求 ..svc.. IN A 的方式发起请求获取 IP 列表,可是须要轮询方式不断获取更新的 IP 列表。
服务治理相关的功能,须要在上述服务治理部分中独立支持。
Pod 的容器中部署的 Dubbo 应用,服务注册流程能够直接删除,服务发现功能经过和 Api Server 进行交互,获取 Pod 和 service 信息,同时 watch pod 和service 的变动。经过这种方式以后,服务治理相关的信息也能够经过 Api Server 直接获取。
Dubbo 能够直接使用指定 ip+端口 的方式调用同一个 pod 下 Envoy (也多是同一个node的Envoy)。Dubbo 将路由规则,负载均衡,熔断等功能交给 Istio 和 Envoy。Envoy 须要支持 Dubbo 协议的转发。
因此 Dubbo 须要完成两个事情:本地 IP 直连(现有功能), 多余功能裁剪(暂未实现)。
全部的服务注册经过k8s的机制完成,全部的服务发现经过 Headless service 完成。sidecar 在建立过程当中,须要对原有的 K8s service 进行 update;
Nacos 做为 Dubbo 的注册中心,而且须要将 K8s 中的数据进行部分中转。Dubbo 应用,将服务注册以应用维度注册到 Nacos ,Istio Pilot 须要识别 Nacos 数据;Istio 的运行机制基本不变,须要将 K8s service instance 的数据写入到 nacos ,供 Dubbo 调用。
Istio 提供了跨集群和云上云下的解决方案, kubeFed 做为 K8s 的跨集群解决方案也能起到必定做用。
这个课题的复杂度更加高,心中有了一些答案,指望你们经过上文也有必定的思考。
抛出三种方式,供你们思考。
Dubbo 原有的服务查询是针对接口的查询,每一个接口会有版本号和组别。接口名+版本号+组别肯定惟一的服务集合,这个服务集下有对应的服务提供者和服务消费者(接口级依赖),服务提供者是一组 ip+port 列表,服务消费者也是一组 ip+port 列表。
当作了改形成应用级别的服务注册或者直接使用 K8s 自带的 Pod 发现机制的话,须要作一些改造,这部分改造,和前面提到的同样,放到其余文章里单独说明。
和 Spring Cloud 相似,支持应用维度的查询。查询到具体应用以后,应用详情下包含了 ip+port 列表,每一个 ip+port 其实就是一个应用的实例。点击开每一个应用实例,能够查看每一个应用的详细信息,详细信息包含了该实例提供了哪些服务。
在原来只支持应用查询的基础上,增长一步:支持查询某个接口对应的应用列表,而大部分接口只属于一个应用。
再点击应用列表下具体的应用以后,会跳到应用详情。
上述讨论的是开源的方案,因此相对历史包袱比较少。对一些大公司想从原有的 RPC 方案切换到云原生的支持,须要考虑更多兼容性和性能,须要付出更大的代价。
云原生的趋势已经势不可挡,在 RPC 领域究竟哪一种方案最终可以胜出,如今还言之过早。我相信 Service Mesh 和传统的 RPC (Dubbo/ gRPC) 都会有本身的一席之地,一切让时间给咱们答案吧。
“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的技术公众号。”