探索Go语言中的微服务框架:Dubbo-go

发表时间: 2023-04-12 12:06

本文介绍了Go 微服务体系发展与选型,过去一年Dubbo-go 社区的飞速发展以及对未来的展望。

作者 | 牛学蔚(蔚俊)

来源 | 阿里开发者公众号

作者简介:

牛学蔚(GitHub: @justxuewei):Apache Dubbo PMC,对云原生、中间件、容器等领域有浓厚兴趣,活跃在 Dubbo 和 Kata containers 两个开源项目中。


1 Go 微服务体系发展与选型

随着微服务技术的快速发展,其在各个领域都形成了一系列事实标准,在 Kubernetes 和容器技术加持下,云原生微服务已经成为了主流解决方案。而 Go 语言作为云原生领域最受欢迎的开发语言,正被越来越多的企业作为微服务开发的首选语言,其中比较流行的包括 Go-micro、Go-zero、Dubbo-go 等。作为 Dubbo 微服务体系中多语言实现的一员,在 2022 年 Dubbo-go 以微服务领跑者的角色积极拥抱云原生标准,探索了 Proxyless Mesh 形态,配合适配 Pixiu 云原生网关,形成了完善的 Dubbo-go 为服务生态矩阵。

以 Dubbo-go 为中心的微服务体系在多个知名企业中成功落地和实践,框架的稳定性在实际场景下经受住了考验。截止今年已有 60+ 家企业在我们的用户列表中登记,其中较为典型案例请参考文章《小米电商 Apache Dubbo-go 微服务实践》。小米电商选用了 Dubbo-go + Nacos + sidecar + etcd + mirpc 为核心的微服务体系,除了看中了 Dubbo-go 的互联互通和服务治理能力外,也认可 Dubbo-go 在微服务方向的沉淀和积累。


2 Dubbo-go 简介

2.1 什么是 Dubbo-go

Apache Dubbo 是一款易用、高性能的 WEB 和 RPC 框架,同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力、工具与最佳实践。Dubbo3 从设计上不绑定编程语言,社区目前提供了 Java、Go、Rust、Node.js 等多语言实现,在未来,我们计划为所有主流语言提供对等的微服务开发体验。

Dubbo 框架作为国内最具影响力的开源微服务开发框架之一,拥有非常高的关注度和活跃度,在 GitHub 上拥有 3.8 万+ stars。Dubbo 项目于 2017 年捐赠给 Apache 基金会,在经历了短短 15 个月孵化后顺利毕业,在 Apache 基金会管理的全部项目中关注度排名第三(前两名分别是 echarts 和 superset),Dubbo-go 作为 Dubbo 多语言生态的重要一员,很好的兼容 Dubbo 生态的同时提供面向 Go 语言体系的微服务开发体验。

Dubbo-go(项目地址
github.com/apache/dubbo-go)作为 Dubbo 多语言生态的重要组成部分,目前完全兑现了 Dubbo3 架构的核心能力,并且在云原生时代,凭借 Go 语言无需重量级虚拟机、静态编译以及垃圾回收的特性,获得了广泛关注,其应用规模也逐渐增扩大。从特性上来说,Dubbo-go 目前支持 HTTP/2、TCP、gRPC 协议通信、服务发现、流量管控、配置管理、全链路追、可视化观测等诸多新特性,Dubbo3 已经众多用户生产环境首选的微服务框架(用户列表);在生态建设方面,Dubbo-go 适配了包括 Zookeeper、Nacos、Sentinel、Zipkin、Kubernetes、Prometheus、云原生 API 网关项目 Dubbo-pixiu、异步网络库 Dubbo-getty、Hessian2 等生态项目。

2022 年 Dubbo-go 社区以生态互联、开发者体验、稳定性为切入点,不断优化系统架构,社区荣获多个开源奖项:

  • Dubbo 生态被评为 2021 年中国 20 大最活跃社区之一
  • Dubbo-go 入围 2021 年“科创中国”榜单。
  • Dubbo-go 开源社区被 OSCHINA 评为“2022 年度 OSCHINA 优秀开源技术团队”。


2.2 重要特性

通信协议:遵循 Dubbo 核心架构设计,Dubbo-go 在实现上不绑定通信协议,目前支持 HTTP/2、TCP (Dubbo2)、JSONRPC、gRPC、HTTP 等多种通信协议,开发者可以根据使用场景灵活的选择通信协议。

服务注册:支持 Client-based 服务发现机制,支持注册中心适配如 Nacos、Consul、Zookeeper 等。Dubbo3 的服务发现机制诞生于阿里巴巴超大规模微服务电商集群实践场景,其在性能、可伸缩性、易用性等方面的表现大幅领先于业界大多数主流开源产品。

配置中心:Dubbo 配置中心可实现应用配置的远程托管,支持配置变更的实时感知,目前支持 Nacos、Apollo(携程开源)、ZooKeeper 等作为配置中心。

负载均衡:Dubbo 提供了多种负载均衡策略,如随机负载均衡策略、一致性哈希负载、基于权重的轮询、最小活跃度优先、自适应负载均衡 P2C 等。

流量控制Dubbo 的流量管控规则可以基于应用、服务、方法、参数等粒度精准的控制流量走向,基于此可灵活的实现超时时间调整、开启访问日志、金丝雀发布、参数路由、同区域优先、按比例流量分发等。除此之外,通过接入 Hystrix、Sentinel 等,Dubbo-go 还支持自适应限流、限流熔断等。

分布式事务:支持 Seata-golang 分布式事务框架,实现了 AT 模式和 TCC 模式分布式事务的调用,AT 模式相较 TCC 模式对代码的入侵性更小、需要开发的接口更少,但 AT 模式对事务操作的数据持有全局锁,TCC 模型性能更好。

链路追踪:支持基于 Jaeger、ZipKin 的链路追踪能力。

指标可视化:支持使用 Prometheus 收集框架指标和用户指标。

可扩展性:Dubbo-go 提供了灵活的 extension 扩展机制,用户可随时根据自己的需求灵活扩展服务发现、负载均衡、配置中心、流量管控规则、全链路追踪等中间件。


3 过去一年我们做了什么

3.1 优雅上下线

在微服务场景下,业务是以容器的形式对外提供服务,k8s 能够方便的对 Pod 进行滚动升级,在旧版本被替换的时候应该达到无损下线的效果,即容器不能被销毁直到没有正在处理的请求。如果其不能被正确实现,对于承载高流量的在线服务来说,在更新期间可能会导致大量的请求报错,甚至可能触发报警,其影响是巨大的。优雅上下线功能是 Dubbo-go 3.0 正式版本发布后的第一个重大增强,王晓伟同学(GitHub: @XiaoWeiKIN)贡献了全流程的优雅上下线能力。

Dubbo 经典的调用流程如上图所示,这里面包含了服务提供者(Provider)、服务消费者(Consumer)以及注册中心(Registry)三个关键组件,一个服务能够被调用,首先需要提供者准备服务并对外暴露端口(步骤 0),然后提供者需要将调用信息注册到注册中心中(步骤 1),消费者则会通过异步订阅的方式获取最新的提供者数据(步骤 2),注册中心在有新数据后会主动推送给消费者(步骤 3),此时消费者已经有本次调用的全部信息了,最后消费者发送调用请求(步骤 4),这样就完成了整个调用链路。

在单体应用中,上述逻辑非常清晰和简单,但是在大规模微服务集群中,这个逻辑的每一个细节都需要被仔细推敲后,才能保证上下线的过程中调用不出错。

优雅上线的目标是解决服务上线调用报错的问题,主要针对微服务场景下的调用依赖问题。在 Dubbo 生态中,Service 表示一个服务,能够被暴露并被其他服务调用,Reference 表示引用,可以简单的理解为下游服务。一个典型调用结构如上图所示,该服务对外暴露一个接口,同时引用了下游的 N 个服务。该服务在上线的时候应该严格遵循以下流程,首先保证下游服务的引用被成功初始化,之后再初始化 Service 对外暴露服务,最后再向注册中心注册服务。优雅上线相对来说逻辑比较简单,只需要严格遵循初始化过程的依赖关系就能保证上线过程中服务能够被正常调用。

优雅下线是优雅上下线的难点,涉及到了信号监听、反注册、等待已有请求完成调用等逻辑。在需要销毁容器的时候,kubelet 会向容器发送 SIGTERM 信号,Dubbo-go 会进入优雅下线流程,此时容器并不会立刻被销毁。即将下线的提供者首先会执行反注册,即向注册中心中删除自己的信息,消费者可以通过订阅获得这个信息,这个过程需要一定的时间。换句话说,在消费者获得这个删除信息之前,流量还是有可能会流向该提供者,此时提供者应该拒绝这部分请求。当然除了下线期间的新请求外,还有残留的来自上游的请求以及自己调用下游的请求,我们分别为这两种情况设置一个计数器,当两个计数器都被清零时,可以认为该提供者是“干净”的。Dubbo-go 的策略是拒绝新请求,等待已放行的旧请求。最后,销毁协议并关闭监听,该容器就能够被安全的摘除。

在启动优雅上下线后,集群内无错误请求,成功率保持在100%。


3.2 新一代柔性服务

在去年发布 Dubbo-go 3.0 版本的时候,柔性服务首次作为一个重要特性被提出。时隔一年,我们带来了全新升级的新一代柔性服务,在新版本中我们将爬山算法替换为峰值干预算法,在经过多次测试后新算法行为可控性更高、性能更优益,这部分工作由来自北京邮电大学的张业鹏同学(GitHub: @CoolIceV)贡献。

柔性服务是一种更智能的负载均衡算法。传统负载均衡算法大多是基于消费者视角,它们共同的局限性是无法根据服务提供者的当前状态动态调整分流策略,如 RR、hash 等算法。这些算法总是以尽可能公平的概率分配流量,但在实践中公平不等于负载均衡。

爬山算法是一种容量预估的算法,服务提供者需要将一些关键信息回传给消费者,比如时延、请求排队数量、预估容量等,消费者使用 P2C 算法选择一个负载最低的作为本次请求的提供者。这些数据实效性要求非常高,如果这些数据是被动传递的,那么很难保证实效性,如果这些数据是被主动探测的,那么在一个大型集群下感知成本非常高。基于上述问题,我们选择了更可控的峰值干预算法。

消费者部分中,我们使用了改良版的 P2C 算法,采集的指标包括请求数(requests)、成功数(accepts)、请求时延(rtt)。与原实现方案不同的是,该版本采用了更合理的滑动窗口(SlidingWindowCounter)和指数移动平均(EMA)两种带有时序性的模块进行采集。

SlidingWindowCounter 会保存时长为统计周期 T 的数据,整个周期内的数据被分割为若干个 Bucket,每个Bucket 保存计数时长内的数据,当前所处的 Bucket 会随着时间前进而向后移动。

EMA 利用指数移动平均算法进行平滑、减小抖动,适用于统计时延型的指标,计算公式:

以下为一个客户端请求 3 个服务端的测试结果,3 个服务端配置不同,分别为 1 核 1GB、2 核 2GB、3 核 3GB。兰青色虚线代表开始使用上述负载均衡算法,可以看到开启前每个服务端接收到的请求数几乎相同,开启之后流量会根据提供者的规格进行智能分流。

提供者基于一个 AutoConcurrencyLimiter 组件限流,在请求到达时会判断已接受的请求是否超过最大处理量,如果超过了就会直接返回失败,限流导致的失败会影响负载均衡时的成功率,进而影响该实例被请求的可能性。与常规限流组件不同的是,该组件会根据采样情况自动调整服务的最大处理量,不需要手动配置,而且增加了 CPU 负载作为启动开关,可以减少被错误限流的数量。

该组件主要关注 QPS、无负载时延(NoLoadLatency)和最大并发量(maxConcurrency),同时有一个用户指定的超参数 exploreRatio,表示探索最大并发量的程度。更新规则是

  • 使用总请求数(包含未被采样的数据)计算 QPS,新 QPS 更大时直接替换,更小则使用指数移动平均进行平滑处理。
  • 使用采样数据计算平均处理时延来估算 NoLoadLatency,平均时延变小才会更新,并使用指数移动平均进行平滑处理。
  • 增加探索因子启发式计算 maxConcurrency, 在采样时延或 QPS 在探索允许的范围之内时会逐步增大 exploreRatio,否则用指数移动平均的方式进行平滑处理

最后可以提供给用户一个可以通过 cgroup v1 进行 CPU 限制,当当前提供者 CPU 负载过高的时候,会无条件拒绝一切新请求。CPU 限制是一种最坏情况下的兜底策略。

以下为 1 个客户端请求一个服务端的测试结果,该测试随着时间推移,QPS 会逐步增大,如蓝线所示。可以看到当CPU 负载(橙线)过高时,有请求被限流(黄线),随后即使 QPS 再增大,CPU 负载、请求成功数均已相对稳定。


3.3 Dubbo Mesh

今年 Dubbo Go 社区发布了 Dubbo Mesh [1] 架构的完整实现,能够以 Proxyless Mesh 的形式加入 Istio 服务网格,开启了 Go 语言体系下的微服务新形态。

Istio 在架构层面分为控制平面(control plane)和数据平面(data plane),其中控制平面是一个名为 istiod 的进程,网络代理是 envoy 。Istiod 简体 Kubernetes 资源(resources)获取服务信息,比如 Service、Endpoint 等,将这些信息通过 xDS 协议发送给位于数据平面的 envoy。Envoy 作为一个独立代理进程以边车(sidecar)形式运行,该进程与业务进程共同加入同一个网络,劫持业务流量并转发到正确的位置。

服务网格能够屏蔽复杂的服务治理细节,让开发者能够专注于业务实现。Istio 通过边车的形式实现了业务逻辑的无侵入性,降低了系统之间的耦合性,带来开发便利的同时也引入了转发时延、额外资源消耗的问题。但是 Istio 作为云原生时代的标杆产品,其架构模式和思路就有非常大的借鉴意义,针对上述提到的 Proxy Mesh 的弊端,我们提出了一套基于 Dubbo-go 的 Proxyless Mesh 微服务治理模式。


剩余60%,完整内容请点击下方链接查看:

Go 语言体系下的微服务框架选型: Dubbo-go


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。