Dubbo 整体介绍
Dubbo 是一款高性能,轻量级的 Java RPC 框架。虽然它是以 Java 语言来出名的,但是现在我们生态里面已经有 Go、Python、PHP、Node.JS 等等语言。在 GitHub 上,https://github.com/dubbo 下面已经有很多生态相关的东西。
Dubbo 是一个 RPC 框架,它和所有的 RPC 一样,有一个最小运行子集,它需要 Provider、Consumer,以及一个服务注册发现相关的东西,在 Spring Cloud 里面是叫服务注册发现,在 Dubbo 里面我们叫它注册中心(后面讲到东西我们都以注册中心来进行说明)。
简单介绍一下 Dubbo 的整个启动过程:
Provider 导出一个服务,这个服务就是可被调用的;
第二步,往注册中心注册这个服务;
Consumer 这端会来订阅相关的服务,如果注册中心里面,Provider 列表有变化的话,它也会得到通知;
Consumer 会根据一定的路由规则从注册中心拿到 Provider 列表,再根据一定的负载均衡策略,精确地调用到某台 Provider 上去。
这就是一个简单的一个 RPC 的调优过程。
Dubbo 在 2011 年就在 GitHub 上进行了开源,经历了很多年的发展,整个社区一直非常活跃,现在 GitHub 上 Star 数已经高达 23K+,Fork 数 16K+。
在 2018 年 2 月份的时候,阿里巴巴已经把 Dubbo 的项目捐献给了 Apache 社区,希望更多人能够参与到 Dubbo 开发中来,希望依靠集体的智慧让 Dubbo 变得越来越好。现在 Dubbo 的 committer,外部开发者的人数已经多于阿里巴巴开发者,包括微店,网易云音乐,考拉,韩都衣舍等等。
Dubbo 因为开源这么多年,积累了较多的用户,包括很多互联网的企业,包括阿里巴巴,考拉,滴滴等互联网企业;还有很多中字头企业,中国电信,中国人寿,中国工商银行;还有一些比较传统的企业。
Dubbo 的实践和演进
Dubbo 服务注册
背景知识介绍
在 RPC 整个链路中,需要的元素有 Provider、Consumer,以及注册中心(中间 Zookeeper 是作为注册中心来使用的)。整个注册过程如下:
Provider 会把一长串 URL(dubbo://xxx 的字符串)写入到 Zookeeper 里面某个节点里面去。
Consumer 的注册也是类似,会写到 Zookeeper 里面某个节点(Consumer 写入的原因,是因为 OPS 服务治理的时候需要实时的消费者数据)。
Consumer 发起一个订阅,订阅相关的服务。
当某个服务的 Provider 列表有变化的时候,Zookeeper 会将对应的变化通知到订阅过这个服务的 Consumer 列表。
从图中我们可以看到 Provider 端的 URL 非常长,特别是当一个服务有大量方法的时候。Provider 端的 URL 会先从 Provider 到 Zookeeper,再往 Consumer 传递,这样导致了单次传输的网络开销比较大。
那么再来看一下集群的情形,图中左边有 N 个 Provider,右边有 M 个 Consumer,那么 Provider 发布的时候,会遇到什么情形呢?Provider 每次发布它会先下线再上线,所以每个 Provider 发布的时候,Provider 会发送两次通知,也就是发送 2N 次;接收数据方有 M 个 Consumer,最后算出在整个网络里面的推送数据的次数是 2N×M。
案例
来看一个真实的案例,在杭州有一家中等规模的电商公司,公司内部有 4000+个服务,以 Zookeeper 作为注册中心,Zookeeper 有 100w 个节点,在发布日的时候,公司内部网络的网卡被打爆了,进而导致服务变更的推送失败,新的服务注册也失败。整个集群基本上处于不可用状态。同样的也收到了一些中小公司的反馈,每次在发布的时候,网络也会有个抖动。
分析一下为什么会出现这种情形。
Zookeeper 的 100 万节点中,大约有 10 万个 Provider 节点和 50 万个 Consumer 节点。按照前面的算法,在所有 Provider 同时发布的极端情况下,有 2×10 万×50 万次推送,也就是说会产生 1000 亿条的数据推送。针对每次推送的数据进行了一个统计,每条 URL 大小大概有 1KB,那么计算出来的极端的推送数据量是 1KB 再乘以 1000 亿,已经是 100TB 的级别了。
上面说的是极端情形,就算是发布日也不可能同时进行发布:有的应用发布日不发版本,不同应用不可能同时发布,同一个应用也需要分批发布。假设同一时刻发布的量在千分之一,那么推送的数据量也在 100GB,所以出现发布日的时候间断性地网卡爆掉的现象就不足为奇了。每次发布的时候,都会想着要跟别的应用发布时间错开,争取单独发布,作为程序员还要纠结这个事情真是一个悲剧。
案例分析
来分析下现在的问题和需求:
首先,根据上述案例中的数据分析得知,性能出现了问题。推送的数据量非常大,存储的数据量大,网络传输量大,服务推送延迟,网卡堵塞,服务注册不可用。
接着对 Provider 端那个很长的 URL 进行分析之后发现,不需要把整个 URL 写到注册中心里,只需要把 IP 的端口写进去就可以了,因为只有 IP 的端口需要实时变化。把其他信息放到一个类似的 KEY-VALUE 结构的持久化存储里去,而且这个 KEY-VALUE 结构只要是应用级别就行了,节省了大量的存储空间。
社区中对服务测试的需求非常强烈。要支持服务测试需求,就需要知道调用的服务方法名,入参出参的详细信息。所以这部分信息也是需要存储下来的。但是这部分信息非常大,每个服务中可能有 10 多个方法,每个方法可能有三四个方法入参,入参和出参的完整数据结构往往非常复杂。这部分数据信息也叫做服务的元数据信息。
首先来看一下怎么解决性能的问题。主要有两种方式可以解决:
怎么减少当次的注册量,就像前面分析的,只存储 IP 的端口到注册中心;
是否可以减少推送的次数,现在推送次数太大了。
减少单次推送量
查看上图可知,Provider 端 URL 还是很长,期望简化往注册中心注册的信息;同时服务测试需求,又同时期望能将更丰富的元数据信息进行持久化的存储。
Provider 端写入的改造。Provider 往注册中心写的时候,将整个数据的写入分成两部分:
写入注册中心;
写入元数据中心。
注册中心作为服务的注册和发现,更加关注数据的实时性和有效性(watch 机制),整个 URL 中 IP 和端口就能判断某个服务是否可用,其他信息都是相对固定不变的。所以注册中心中,只需要存储 IP 和端口。元数据中心中存储 URL 中除 IP 和端口外的其他信息,加上服务测试需要的服务方法名,服务方法的出入参信息。元数据是一个 KEY-VALUES 的持久化存储,是独立于注册中心的存储,它不需要有 watch 的机制,而只需要提供持久化存储。图中使用的的 KEY VALUE 存储是 Redis,但是元数据中心定义了一套 SPI,开发者可以去扩展,可以自己实现 DB 存储,或者其他持久化存储的方式。
Consumer 端获取 Provider 列表信息的改造。Dubbo 之前的版本中,直接从注册中心里面获取 Provider 端的服务信息,获取到的信息已经是一个完整的可调用的服务信息。但是 Provider 端写入改造之后,原有 Consumer 端获取的 Provider 服务信息的方式不可用了。除了从注册中心获取到的数据之外,还需要从元数据中心里拿到元数据信息,然后对这两部分数据做一个 Merge 之后才能构建出完整的可调用的服务信息。
当前 Dubbo2.7 版本还没有完全去除所有参数,而是采用先去除部分参数的方式来验证;后续会逐渐迭代完善,同时在 2.6.x 版本中也会进行一些兼容方案的支持。
应用级服务注册
上面的改造针对的是怎么减少单次的推送数据量,针对的还是服务维度。期望中最理想地给注册中心减负的方式是应用维度的服务注册和发现,可以参考 Spring Cloud 体系下的 Eureka 实现。一旦实现这种方案,服务注册中心就再也不会成为 RPC 领域的瓶颈,而且可以认为这种方案是服务注册的终极方案。
当然这种实现方式做的改动相对比较大,不仅需要将服务执行和运维完全分开,而且需要一定的架构体系改造来支撑具体服务的发现。到目前为止还没有形成成熟可靠的方案,团队内部也只是在探讨阶段。
服务变更推送开关
所谓服务变更推送开关,就是针对任何的服务信息的变更,不进行推送。
到底哪种情形需要这种开关呢?阿里巴巴整个集群的机器数非常大,所以宿主挂掉或者虚拟机挂掉出现的概率比较高。在每年双十一的时候,大部分消费者都会去淘宝天猫上购物。在 11 月 10 号 11 点 50 几分开始,大量买家在拼命地刷新购物车或者商品详情页面,这时候阿里巴巴内部的系统负载是非常高的,网络负载也非常高。如果这时候,有一台机器因为宿主机挂了的原因而导致部分服务下线,这时候需要推送相关应用服务下线的变更给对应的服务 Consumer。这时候就需要占用网络带宽,可能对服务调用产生影响,进而还会对双十一造成很大的压力。所以这时候就希望有一个开关,能够把整个服务推送关掉。
但是这时候也会带来一些问题,当服务 Provider 不可用的时候,注册中心没有向服务 Consumer 推送变更通知,服务 Consumer 请求的时候可能会报错,这时候的小部分服务出错可以允许的;保证整个集群上万台机器,特别是整个双十一核心链路的稳定性才是双十一最重要的使命。
服务分组
在一个大的集群环境中,在没有路由规则的情况下,Consumer 集群会调用整个 Provider 集群中的任何机器。服务分组,就是对 Consumer 集群和 Provovider 集群进行分组,将大的服务级分成几个子集。
举个例子,集群中有 8 个 Consumer 实例,有 8 个 Provider 实例,按照正常流程 Consumer 这 8 个实例会调用 Provider 任何一台,任何一个 Provider 的变更通知也会通知到这 8 个 Consumer 实例。但是如果对它进行分组呢,Consumer 实例集群分成 A 和 B 两个组,Provider 集群也分成 A 和 B 两个组。Consumer 中 A 的组只能调到 Provider 中 A 组的服务;Provider 的 A 组中的实例在发布过程中,也只会推送到 Consumer 的 A 组中,而不会推动 Consumer 的 B 组。最终通过推送的范围,来减少了推送的数据总量。
对于服务分组的的实现,这里不做展开,本文后面再次讲到服务分组的时候,会稍微展开陈述。
服务注册中心
前面陈述的服务注册相关的改造和方案,都是围绕 Dubbo 等 RPC 进行的。接着来看一下在互联网环境下,理想的注册中心是什么样子的。(以下阐述只代表个人观点)
CAP 理论:现在大部分主流而且在使用中的注册中心都是满足 CP 的,但是在互联网大集群环境下,期望的结果是满足 AP 的同时,能够满足最终一致性。在大集群环境下,可用性往往比强一致性的优先级更高。以 Zookeeper 为例,Zookeeper 能够为分布式系统提供协调功能的服务,默认提供强一致性的数据服务,但是它在某些情况下是允许 Zookeeper 是不可用的。列举一个场景,Zookeeper Leader 失效了,这时需要重新选举 Leader,而这个选举过程需要 30 秒以上(数据来自于网上的文章),这段时间内 Zookeeper 对外是不可用的。
去中心化:Zookeeper 是有 Leader 机制,往 Zookeeper 里写数据都是往 Leader 里面写,这个 Leader 其实就是一个单点。所以整个写的过程是中心化的。而且 Zookeeper 对跨城跨机房的方案上,支持非常有限。
数据推送的强控制:期望对推送的有更加强的灵活性。还是以 Zookeeper 为例,Zookeeper 中有 watch 机制,每个数据节点发生变更的时候,就会往外推送变更的通知。但是作为注册中心,我们期望能够控制它的推送频率,针对新增节点只需要一分钟里面推送 6 次就可以了,每十秒推送一次,这样可以合并一些变更通知,减少网络数据请求的数据量。
容量:Dubbo 是单进程多服务的方式来注册服务的。这也就意味着注册中心中需要存储的数据量较大,所以要有足够的容量来支撑这种场景。
那些注册中心产品:Zookeeper 作为服务注册中心的公司在减少,那么现在有哪些方案,可以来替代呢?
Eureka 是一个 AP 的应用,而且它是去中心化的。但是它有几点不足:
在我们的内部的性能测试中,它性能表现非常一般,性能大概只有 Zookeeper 的 60%左右。
Eureka 内有一种契约机制,它每隔 30 秒会发起一个续约的请求,如果 3 次没有接收到,它才会过期失效;如果一个服务非正常退出(没有发起解约请求),那么就存在这个超时的间隙期,服务是不可用的。所以在生产环境,对服务敏感的相关应用方是无法满足需求的。
Eureka 是应用维度的服务注册,当前的 dubbo 是服务维度的注册,如果要匹配的话,需要大范围改造。
Netflix 宣布了停止更新 Eureka 2.0。
Etcd 是 Zookeeper 的升级版,它参考了 Zookeeper 的很多实现,同时进行了较多优化。Etcd 的强一致性协议和代码实现更加简单,它的部署方式也更加简单,它支持了 Rest 的方式进行相关访问,它的性能相对 Zookeeper 来说也有了一定的提升。但是它还是一个 CP 的系统,它也是要求数据的强一致性,而牺牲部分的可用性。
Consul 相对前面几个产品来说,更加专注服务注册发现本身,它是一个比较专业的服务注册中心。Consul 有了后台管理页面,它有了健康检查,Consul 原生支持多数据中心。但它的性能上有瓶颈的,它和 Zookeeper 和 ETCD 进行对比,它性能是稍微差一点的;同时 Consul 也要求数据的强一致性而牺牲部分可用性。
Nacos 是阿里巴巴开源的一个产品,内部系统也在使用,它已经经受了一定流量和用户的考验。现在阿里巴巴集团内部的 Provider 和 Consumer 数量已经到达了亿的级别,它现在能够支撑上亿级别的订阅量,整体经受了一定的实践检验。Nacos 整体设计是去中心化的,而且设计上满足 AP 和最终一致性,性能上和 Zookeeper 比较接近。
前段时间和网易考拉在沟通过程中也发现,他们也在做一个自己的注册中心;新浪也有一个自己的服务注册中心。所以许多大的互联网公司,因为定制或者差异化的需求,都在自研注册中心。
配置中心
当前困境
应用开发者
Dubbo 内部有一个配置文件叫 dubbo.properties,这种配置方式和 Spring Boot 的 application.properties 是比较像的。每次新开发一个 Dubbo 应用的时候,应用开发者都需要去写一大堆的配置到 dubbo.properties 中,包括注册中心的地址,元数据中心的地址,应用级别的超时时间等等。当所在公司只有两三个应用的时候,一个个应用单独设置的方式是没问题的;当业务快速发展的时候,应用数从 2 个变成 20 个应用的时候,那么所在的技术团队可能需要整理一份快速构建应用的文档,供应用开发者参考使用,而且这个文档需要及时维护更新。如果这时候还需要更改注册中心地址(原来 2 个应用的时候,Zookeeper 的地址用 IP 来快速实现,现在想换成域名了),意味着要去推动这 20 个应用的开发者,让他们修改对应的配置,然后测试并且发布。整个过程非常痛苦!需要有一种类似于 Spring Cloud Config 的配置方式来满足集中式的配置方式,相当于一个远程集中式的 dubbo.properties。
Dubbo 开发者
Dubbo 2.7 以前的版本中,服务路由规则,服务治理规则的数据都是存储在注册中心中的。之前大部分用户都选用 Zookeeper 作为注册中心,Zookeeper 兼具了 Key-Value 的功能,所以之前的版本中运行起来是没有问题的。 但是如果选用的注册中心,不具有持久化的功能,这时候的路由规则和服务治理规则就没地方存储了,整个系统就玩不转了。作为 Dubbo 开发者,期望将服务治理和服务路由规则分开存储到一个集中式的 Key-Value 存储中。
要解决上述两个痛点,需要在 Dubbo 中引入一个远程的集中式的配置中心,这个配置中心存储了远程的 dubbo.properties,路由规则,服务这里规则等。
应用架构师
举个场景,一个公司的所有应用已经引入了 ETCD 作为自己的应用的动态配置管理,但是在引入 Dubbo 以后,Dubbo 中又有几套对应的动态配置可供选择,如 Nacos、阿波罗,Zookeeper。这就要求公司层面需要维护两套动态配置的 Server,这个成本比较高,而且增加了系统的稳定性的风险。对于架构师来说,需要 Dubbo 能支持 ETCD 的动态配置。
解决方案
图中分成上下两部分,下面黄色部分就是它的一个存储;上面的整个部分是 Dubbo 内部实现。上文中所讲的动态配置,在 Dubbo 中定义为配置中心。
在实现层面,在 Dubbo 中定义了一层 SPI,默认实现的 Zookeeper,Apollo,Nacos。应用架构师去扩展这个 SPI 就可以完成 ETCD 的定制,这样就能达到和原有的应用中使用 ETCD 方式兼容。
对于运维工程师来说,原来的一个注册中心的地址变更要推动每个应用开发者去做变动,应用开发者非常烦,运维工程师很吃力。现在只需要在远程的 dubbo.properties 里进行统一去升级,然后通知相关的应用开发者进行一些适当的验证。
因为已经定义了配置中心的 API,Dubbo 开发者直接调用对应的 API 来实现服务规则和路由规则的存储。
三个中心
三个中心就是前面讲到的注册中心,配置中心,元数据中心。
期望的使用方式:Provider 先去配置中心里获取注册中心的地址和元数据中心地址,再根据拿到的注册中心地址去对应的注册中心注册服务,根据拿到的元数据中心地址写入元数据信息到对应的元数据中心 Server。Consumer 和 OPS 也是类似的。
通过配置中心这种集中式的配置方式,可以让开发者从原来烦琐的配置中解脱出来,让它更聚焦于业务的开发,而不需要关注框架层面的东西。
路由规则
路由规则组合
Dubbo 路由规则,按照覆盖范围可分为应用级别,服务级别,方法级别路由规则;按照功能纬度,可以分为黑名单,条件路由 ,TAG 路由规则。大部分路由需求都是可以通过组合来实现的,如应用级的黑名单可以通过应用级别+黑名单路由规则方式组合。
全链路灰度发布
某产品新开发了一个新特性,想进行 A/B Test,让部分用户开放新功能体验;或者产品在迭代过程中,想进行下改造功能的灰度验证。在当前微服务的架构体系下,一个新功能往往依赖整个调用链路的上下游一起完成。所以这两种新功能的验证,基本不可能在单机上完成,往往需要服务链路的上下游一起隔离出一部分机器进行验证。在这里,称为全链路灰度发布验证。
来看一种最简单的场景。客户在浏览器端发起一个 HTTP 请求,这个请求会根据一个负载均衡策略访问到一台 web 服务器(30.5.127.44),这台机器会调用服务 A 集群,服务 A 集群会调用服务 B 集群。业务开发者开发了一个新功能,想在线上验证功能的正确性,但是又不想原有的功能受影响。也就是说想从服务集群里拿出少部分实例去验证线上的功能。假设下需求,希望总用户的千分之五的用户能够走到新功能的流程,帮助验证新功能的正确性。
从服务 A 集群中选出一台机器(30.5.120.16)作为灰度验证的机器,从服务 B 集群中选出一台机器(30.5.128.66)作为灰度机器。对选出的这两台机器打上标 canary(金丝雀)。
需要在 Web 服务器运行的代码逻辑中,增加逻辑:获取到用户的 UserId,UserId 对 1000 求模之后小于 5 的,在 Dubbo 对服务 A 集群发起请求之前带上 Tag=canary(在 Dubbo 中是通过设置 Attachment 来完成)。
Dubbo 自带的 Tag 路由规则会做以下事情:Dubbo 会先拿到要调用服务所在的应用名;再根据应用名+Tag 路由规则的名称 canary,去获取到对应的机器列表,Web 服务就拿到了 30.5.120.16 这台机器;Dubbo 根据拿到的机器列表依据负载均衡策略发起请求。相应的 Web 服务器中没有打上标的机器,会访问到其他机器(30.5.120.26),而不会访问到已经被打上标的机器。Tag 路由规则,完成了对相应 Provider 和相应 Consumer 端的隔离。
通过 Tag 路由规则已经解决了 Web 集群到服务 A 集群这里面的链路,但是怎么解决服务 A 集群到服务 B 集群的 Tag 的传递呢?现在比较流行的一些全链路跟踪的产品可以帮我们做到,如 Open Tracing,Zipkin。我们以 Zipkin 为例,通过 Zipkin 实现 Dubbo 的 Filter 可以把这个标从 Web 集群传到服务 A 集群再传到服务 B 集群,整个链路都可以传递下去。
整体调用链路总结。满足灰度验证的用户链路:web 服务 -> 30.5.120.16 -> 30.5.128.66; 不满足灰度验证的用户(不打标)链路:web 服务 -> 集群中 30.5.120.16 之外的机器 -> 集群中 30.5.128.66 之外的机器。
通过上述步骤已经完成了全链路的灰度环境搭建,这种方式的应用范围非常广,可以按照自己的业务场景和需求进行一些调整来达到自己期望的效果。下面集群中也有案例进行说明。
集群
从集群角度来看下,服务实例之间的隔离。假设一个 Provider 集群有 8 个实例,Consumer-A 集群有 4 个实例,Consumer-B 集群有 4 个实例; Consumer-A 集群和 Consumer-B 都会调用 Provider 集群。
服务分组:以 Provider 和 Consumer-A 为例。将 Provider 分成左(1、2、3、4)和右(5、6、7、8)两个组,将 Consumer-A 分成左(1、2)和右(3、4)两个组。Consumer-A 中左组只能调用到 Provider 中的左组,Consumer-A 中右组只能调用到 Provider 中的右组,而不会调用到整个集群的其他实例。服务分组除了能让注册中心减少推送的数据量外,还能减少 Consumer 和 Provider 长连接的数量,从而带来一定的性能提升。
服务分组最简单的实现方式,在 Dubbo 的 Service 和 reference 中配置中配置多个 group,进行手动匹配。
更高级的服务分组实现方式,通过路由规则的方式进行动态匹配,路由规则中根据一定的规则(如根据 Ip 的最后位的奇偶数)将 Provider 和 Consumer 进行分组,然后路由规则里去完成隔离。这种方式暂时还没有发现较好的实现。
业务隔离:来看个现实的场景,阿里巴巴交易平台承接的业务非常多,在新零售出来之前,所有的业务都是线上交易,在出现 2 分钟的不可下单的时候,体验糟糕但是还不会引起大范围的骚乱。但是在盒马鲜生线下购物的时候,如果出现 2 分钟不能下单,那在排队的消费者意味着要干等两分钟,这是非常糟糕的体验。
抽象下上面的场景,用更加产品化和技术化的方式来分析。以 Provider 和 Consumer-A,Consumer-B 为例。Provider 集群的消费者非常多,包括 Consumer-A,Consumer-B 或其他 Consumer 集群。因为 Consumer-B 的业务非常重要,不想让其他机器的故障,影响到 Consumer-B 的稳定性,所以 Provider 中来自于 Consumer-B 的流量需要进行隔离。业务隔离的实现方式,可以采用 Tag 路由规则的方式来实现。对 Provider 集群的 7,8 机器打上标-BTag(即路由规则,可以通过 OPS 打标),然后在 Consumer-B 集群中调用 Provider 之前 setTag=BTag (在 Dubbo 中在 attachment 里设置 tag 值)。
灰度环境:
单机灰度的话,只要在发布的时候,可以单台机器发布就可以。
全链路灰度,需要在整个集群中圈出一批机器,如从 Provider,Consumer-A,Consumer-B 集群中选出 3 号机器作为灰度环境。全链路灰度的实现已经在前面说明,用 Tag 路由实现。
服务测试
Dubbo 服务测试
Dubbo 在启动时,会导出服务,让服务是可被调用的;同时它还会向元数据中心写入元数据信息(包括服务方法的方法名,路参,出参等信息)。
服务测试是在 Dubbo OPS 后台中的功能。服务测试业务流程:先去元数据中心获取元数据信息;在页面中展示服务的结构体,等待用户输入;用户输入参数信息;Dubbo OPS 后台根据元数据信息和用户输入的参数构造出服务调用的入参,向相应的 Provider 发起泛化调用。泛化调用被较多的网关系统使用。
Swagger 整合
除了服务测试功能外,还需要:
文档功能。自动生成文档,生成的文档是可视化的,而不是 JSON 等文件格式。
自动化测试。
Swagger 可以满足以上需求。但是 Swagger 和 SpringMVC 结合紧密,SpringMVC Rest 接口加上注解之后,可以快速整合 Swagger。
要让 Dubbo 能够通过 Swagger 方式生成文档和进行自动化测试,需要做两个事情:
将 Dubbo 接口形式转换成 Swagger 文档。Swagger 是有文档规范的,只要将接口形式转换成 Swagger 约定的格式,就可以满足 Swagger 文档的形式进行输出。
Swagger 服务测试功能。外部 HTTP 请求到一个 Controller,Controller 需要将 HTTP 请求转换成 Dubbo 请求,包括参数的映射和服务的映射。
服务治理
Hystrix 停更
Hystrix 停更了,在 GitHub 官网上推荐了 Resillence4j,阿里巴巴也开源了 Sentinel。这里进行一个简单的比较。
Hystrix 的两块功能,隔离和熔断,能满足大部分需求但是停止更新了。
Resillence4j 要求 JDK8 及以上,对 function 编程更加友好。
Sentinel 在阿里内部使用的范围非常广,已经经受住了一定的考验。每年双十一在买东西的时候,如果出现一个页面上显示系统繁忙请稍候重试,或者显示人太多了请排队,这时候其实就是 Sentinel 在后台发挥作用了。Sentinel OPS 这块做得非常好,达到了开箱即用的标准。
服务治理清单
不要设置应用级别重试
在 Consumer 端设置重试,必须保证 Provider 端多次调用是幂等的。
设置应用级别的重试,会让已经压力倍增的系统雪上加霜。举个例子,Provider 系统的线程数是 200,当 Provider 某个服务出现问题,导致 RT 从 200ms 变为了 3500ms;Consumer 因为设置了默认重试且重试次数为 3,这就会导致 Provider 线程池很快会被耗尽。
所以,默认不要设置应用级别的重试,对有业务需求的服务单独设置重试的规则,并且保证 Provider 端服务的幂等性。
设置超时时间
设置超时时间,能够防止系统雪崩和资源耗尽。一个 Consumer 调用 Provider 的时候,Provider 需要 3 秒处理完业务逻辑之后返回结果,Consumer 默认是同步调用,需要某个线程同步阻塞等待 3 秒,也就是说如果没有超时机制,很容易将 Provider 的问题传递给 Consumer 并且不断往上传递。如果 Consumer 设置了超时时间 200ms,Provider 加入需要 3 秒处理完结果,但是不会将问题传递给 Consumer。
建议的超时时间是一百毫秒到两百毫秒左右,对于特殊业务进行特殊设置。
隔离
隔离包括实例隔离和线程隔离。实例隔离,就是前面讲到的服务分组和业务分组,通过实例隔离达到问题和影响面的隔离。
线程隔离,可以分为静态隔离和动态隔离。静态隔离,首先通过一定的分析方法找出一些比较慢的方法,或者甄选出业务上比较重要的服务方法;再对这些服务或者方法单独设置线程池以及对应的线程个数。动态隔离是在 Dubbo 应用运行态的时候,自发地调整线程池的隔离。Dubbo 应用里默认的线程数是 200 个,200 个消耗完之后,新的请求进来就会返回线程池满的异常。这时候 Dubbo 内部会对运行中的服务方法进行统计,统计出并发线程数>8,RT>300ms(假设的一个指标)的服务,把这些服务扔到独立的线程池里去完成,后续这些服务的执行都会由这些新建的线程池的线程执行,其他服务仍然在这 200 个线程池里面执行。当这些隔离的服务方法运行一段时间后,它的 RT 变得正常了,那这部分独立出去的线程池可以被销毁,这些服务方法可以重新由原来的 200 个线程执行。整个过程的调整是动态的。整个动态隔离的机制和 Hystrix 的线程隔离有些相同的含义。
治理工具
每个微服务实践的时候都会讲到。微服务最佳实践里面,强链路跟踪,限流,降级、熔断等,都可以在系统里引入,进而保证系统的稳定性。
系统可验证
系统可验证主要包含两方面,第一个是环境是可被验证的;第二个是通过一定的测试手段和方法来保证系统是可测试和可验证的。
环境可被验证,有些场景需要支持单机的灰度,有些场景需要全链路的灰度。
测试方面:在系统上构建接口级别的服务测试,自动化测试,支持全链路压测。通过服务测试和自动化地执行来增强系统的正确性测试;通过全链路压测,来判断全链路的瓶颈在哪里。
Service Mesh
Service Mesh 在 2018 持续火热,阿里巴巴和蚂蚁都在这一块有布局。现在阿里内部主要在两个场景中去做尝试,第一个是跨语言。阿里大部分语言是 Java,但是有少部分是 C++,还有部分 NodeJS,需要 Service Mesh 去解决跨语言之间的调用。第二个是异构系统。阿里巴巴收购了一些公司,但收购进来公司的技术栈和集团内部不一致,它可能是 Java 语言,但是使用的 Spring Cloud 的微服务体系,还有一些公司没有使用 Java 语言,需要通过 Service Mesh 来解决这种异构系统之间的调用问题。
从实现方式上来说,阿里没有重复的造轮子,控制层这一块(也被称为 sidecar),是在 Envoy 上进行了一些扩展,实现了一个 Dubbo Filter,现在这部分代码已经被捐献到了 Envoy 的社区。数据层实现方式是对 Istio 做了一些扩展和定制。因为 Istio 有一套默认的服务发现,服务治理的产品,但阿里巴巴有自己的服务发现,动态配置管理,服务治理的产品方案。在数据层,会将 Istio 和阿里巴巴中间件的产品做一些整合,来满足需求。
从部署结构上看,Dubbo 应用和对应的 Envoy 实现是部署在一台机器上(虚拟机)的,只是以两个进程的形式存在。Dubbo 应用需要调用 Spring MVC 应用中的服务,每一次发起调用的时候,Dubbo 应用会首先调用到同一台机器上的 Envoy 进程,再调用到另外一台机器上的 Envoy 进程,另外一台机器的 Envoy 进程再调用 SpringMVC 应用。整个调用过程多了两跳。
阿里巴巴已经在咸鱼的一些场景上在做 Service Mesh 的验证,同时程序和架构也在持续优化过程中。
Dubbo 现状和未来
当前工作
核心特性
针对 2.7 之前存在的若干异步问题进行了优化。在 Dubbo2.7 之后,开始全力拥抱 JDK8。所以 2.7 版本将基于 JDK8 中的 CompletableFuture 做出一些针对性的增强来支持异步,类似于 Promise 方式。
优化服务治理参数配置,升级服务路由规则,同时对应的 OPS 也进行了相应的升级。Dubbo2.7 之前的路由规则对应关系一个服务可以对应多条规则,优化之后一个服务只能对应到一条规则;新增了前文讲到的 Tag 路由;服务治理参数,路由规则的数据存储都将使用配置中心。
社区里面分支演进分成 2.6 版本和 2.7 版本。2.7 版本主要有一些新功能的迭代,2.6 以维护为主。如果 2.7 里有些功能可以被 2.6 借鉴的话,也会往 2.6 里面叠加。
Spring Boot
新增了一个工具页面,用于快速构建 Dubbo 的 Spring Boot 的应用。这个参考了 Spring Boot 的模式。
在 Dubbo 体系下,有一个 Dubbo Spring Boot 工程来支持 Dubbo+Spring Boot 的整合。到目前为止支持 Dubbo2.6.X 版本,支持 Spring Boot 1.5.x 和 2.0.x。因为 Spring Boot 的 1.5 和 2.0 的分支差距比较大,所以 Dubbo Spring Boot 也维护了两个版本。后续 Spring Boot 1.X 会在 2019 年停止更新。
Dubbo OPS
Dubbo OPS 进行了全新的升级,有了一个新的 UI,合并了 Dubbo Admin 和 Dubbo monitor 两个应用,而且整个 OPS 用了 Spring Boot 工程结构,从而真正达到了开箱即用。Dubbo OPS 能够同时支持 2.6 和 2.7 的版本的运行,对新功能进行了相应的配置支持。
开源建设
Dubbo 捐献给了社区之后,重新搭建了新的官网,新的中英文文档,有了中英文博客,整个社区现在非常活跃。在这半年多依赖,和一些深度使用 Dubbo 的客户做了一些交流,如工商银行,考拉等等。今年举办了 5 场 Meetup,包括杭州,成都,深圳,北京,上海,吸引了大量的开发者参与。
生态建设
正如前面所说,在多语言方面,社区引入了 PHP,Go,NodeJS,Python 等。
对现有的一些生态组件进行持续升级和维护。
引入了一些新的扩展组件,包括 SkyWalking,ETCD 等等。
未来规划
社区投入
社区持续投入,包括 PR,Issue 跟进,优秀博客和文档的持续输出。当然整个运作形式是根据 Apache 的方式进行运作,而 Apache 沟通的最主要形式就是邮件,所以各开发者可以订阅 Dubbo 的邮件列表进行持续跟踪。
持续推进 Dubbo Meetup 和客户交流。
功能增强和 Issue 跟进
持续功能增强和优化,包括前面的注册中心的优化,路由规则的持续增强和优化。现在 Dubbo 的 committer 中,阿里只占了小部分,其中来自于网易音乐和考拉,微店,韩都衣舍,有赞等公司的开发者也非常活跃。最近有一位印度的开发者,也正式成为了 Dubbo 的 Committer。
生态建设
因为现在生态中,部分实现已经有了重叠,包括后续进来到我们生态体系里的扩展也会造成同样的扩展或者实现出现重叠,这时候会进行一些优胜劣汰。
Dubbo 和 SpringCloud 关系,部分同学在选型的时候觉得要么选 Dubbo,要么选 SpringCloud,两者互相排斥的。其实不是这样的,这块的详细描述,可以关注公众号:阿里巴巴中间件。里面会有一些文章的描述和分析。
Dubbo 3.0
Dubbo 3.0 的进展,比较受到关注。因为涉及到 Dubbo 和 HSF 整合,以及一些新特性的开放,如 reactive 的引入。但是整个过程是非常漫长的,因为两个产品不可能完全融合,需要有一些特性存在。而且在完成之后,整个迁移的过程非常巨大。今年上半年,会有一个预览版本,现在很大一部分人力已经投入到 Dubbo 3.0 的研发中。
Service Mesh
Service Mesh,现在还在测试阶段,整体还未达到生产环境的级别。还需要不断优化性能和不断在各个场景中试用。
作者简介
曹胜利,阿里巴巴中间件技术部技术专家。2010 年加入阿里 B2B 中国网站技术部,先后在 1688 技术部,菜鸟技术部负责业务产品;曾负责阿里采购平台的架构和服务化改造工作;目前在阿里巴巴从事应用容器和微服务框架的开发、实施以及效率提升相关的工作。关注分布式架构、微服务,特别是 Spring,Spring boot 生态的发展。
评论 2 条评论