作者:王宇轩, Apache Dubbo Committer
随着 Dubbo3 在云原生微服务方向的快速发展,Dubbo 的 go 语言实现迎来了 Dubbo3 版本以来最全面、最大幅度的一次升级,这次升级是全方位的,涉及 API、协议、流量管控、可观测能力等。
总的来说,新版本的 dubbo-go:
- 全面升级 Triple 协议, 兼容 gRPC、标准 HTTP 客户端,提供简单明了的 API 用于编写 RPC server 与 client,解决组件间的基本通信问题。
- 针对微服务场景,提供了完善的服务治理能力, 这包括配置管理、可观测性、流量管控规则、生态集成与适配等的全面升级。
全新升级的 Triple 协议
基于 dubbo-go 实现的 Triple 协议,你可以轻松编写浏览器、gRPC 兼容的 RPC 服务,并让这些服务同时运行在 HTTP/1 和 HTTP/2 上。
如上图所示,你可以使用 “http+json” 的标准形式访问 dubbo-go 发布的后端 triple 服务,基于这一特性, 我们可以在 dubbo 客户端在浏览器页面、移动设备上访问后端服务,使用标准 cURL 工具访问服务,也可以让比如 Spring 体系的应用轻松的调通 Dubbo 服务。
由于 Triple 协议完全兼容 gRPC 协议,Dubbo 后端服务有可以直接调通标准的 gRPC 服务,它们之间可以无缝的互通,不论是 unary 还是 streaming 通信模式。
发布一个 triple rpc 服务
为了体验升级后的 triple 协议,我们接下来会尝试启动一个 dubbo-go server,并发布一个基于 triple 协议的服务。
创建一个新的 server 并启动 server,它将在指定的端口监听 triple 协议请求。
func main() {
srv, err := server.NewServer(
server.WithServerProtocol(
protocol.WithTriple(),
protocol.WithPort(50051),
),
)
if err != nil {
panic(err)
}
if err := greettriple.RegisterGreetServiceHandler(srv, &api.GreetTripleServer{}); err != nil {
panic(err)
}
if err := srv.Serve(); err != nil {
panic(err)
}
}
cURL 访问服务
Triple 服务启动完成之后,最简单方式是使用 HTTP/1.1 POST 访问服务,参数则作以标准 JSON 格式作为 HTTP 负载传递。如下是使用 cURL 命令的访问示例:
curl \
--header "Content-Type: application/json" \
--data '{"name": "Dubbo"}' \
http://localhost:50051/greet.GreetService/Greet
Triple 协议的一项重大升级是支持标准 http 工具直接访问,通过 cURL 可以极大的降低 dubbo-go 服务的测试验证、前端接入成本。
简单明了的 API
Dubbo Go SDK 支持使用 IDL 或编程语言特有的方式定义服务,并提供一套轻量的 API 来发布或调用这些服务。在上一节的示例中,我们已经看到了部分 dubbo-go API 的使用方式,接下来,让我们更仔细全面的看一下新版本的 API 设计。
RPC Server 与 Client
对于一些 RPC 通信的场景,开发者只需要使用 dubbo-go 编写一个最简单的 RPC server 或者 RPC client,这在新版本 dubbo-go 中只需要几行代码即可完成。
通常,我们会使用 Protocol Buffer (IDL) 来定义一个 Dubbo 服务。
syntax = "proto3";
package greet;
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
使用 Protocol Buffers Compiler 从 IDL 生成 stub 代码(篇幅关系,我们不在此展示,具体请参见官网 dubbo-go 快速开始)。接下来,我们实现 greettriple.GreeterClient 接口并提供自定义服务实现。
type GreeterServer struct {
}
func (s *GreeterServer) SayHello(ctx context.Context, in *greet.HelloRequest) (*greet.User, error) {
return &greet.User{Name: "Hello " + in.Name, Id: "12345", Age: 21}, nil
}
以下是一个简单的 RPC server 示例,执行协议信息,并注册服务到 server 中:
func main() {
srv, err := server.NewServer(
server.WithServer_Protocol(
protocol.WithTriple(),
protocol.WithPort(50052),
),
)
if err != nil {
panic(err)
}
if err := greettriple.RegisterGreetServiceHandler(srv, &api.GreetTripleServer{}); err != nil {
panic(err)
}
if err := srv.Serve(); err != nil {
panic(err)
}
}
如前面 Triple 协议一节所述,你可以使用 cURL 直接测试以上 server 服务运行正常。与此同时,对应的 RPC client 示例如下:
func main() {
// for the most brief RPC case
cli, err := client.NewClient(
client.WithURL("tri://127.0.0.1:50052"),
)
if err != nil {
panic(err)
}
svc, err := greettriple.NewGreetService(cli)
if err != nil {
panic(err)
}
common.TestClient(svc)
}
微服务开发
如果你正在开发微服务应用,那么除了 RPC 通信之外,你通常还需要为应用配置一些服务治理能力,比如 retistry 注册中心、配置中心、可观测能力等。
以下展示了如何使用 dubbo-go 开发一个微服务应用。
首先,创建一个代表微服务的应用 Server,将服务注册给它,添加注册中心等服务治理配置。
func main() {
// configure global configurations and common modules
ins, err := dubbo.NewInstance(
dubbo.WithName("dubbo_test"),
dubbo.WithRegistry(
registry.WithZookeeper(),
registry.WithAddress("127.0.0.1:2181"),
),
dubbo.WithProtocol(
protocol.WithTriple(),
protocol.WithPort(50052),
),
)
// create a server with registry and protocol set above
srv, err := ins.NewServer()
if err != nil {
panic(err)
}
// register a service to server
if err := greettriple.RegisterGreetServiceHandler(srv, &api.GreetTripleServer{}); err != nil {
panic(err)
}
// start the server
if err := srv.Serve(); err != nil {
panic(err)
}
}
其中,Instance 是我们在新版本中引入的全局配置,你可以将所有微服务全局配置在这里进行初始化。这里,我们设置了微服务通信协议 protocol 和注册中心 registry,如以下代码片段所示:
ins, err := dubbo.NewInstance(
dubbo.WithName("dubbo_test"),
dubbo.WithRegistry(
registry.WithZookeeper(),
registry.WithAddress("127.0.0.1:2181"),
),
dubbo.WithProtocol(
protocol.WithTriple(),
protocol.WithPort(50052),
),
)
接下来的操作就非常简单明了了,我们创建一个 server,将服务注册给它并启动,如下所示。如果有更多的服务,则可以依次注册到 server 后再启动。
// create a server with registry and protocol set above
srv, err := ins.NewServer()
// register a service to server
if err := greettriple.RegisterGreetServiceHandler(srv, &api.GreetTripleServer{}); err != nil {
panic(err)
}
// start the server
if err := srv.Serve(); err != nil {
panic(err)
}
以上就是一个微服务应用的基本开发过程,如果你的微服务应用要调用一些远程 Dubbo 服务,那么你只需要参照以下方式创建一个 client 就行了。
下面的代码示例创建了一个 client,紧接着生成了一个 GreetService 远程服务代理,之后,就可以像调用本地方法一样调用远端 Dubbo 服务了。client 将基于注册中心实现 server 实例的自动发现并自动为流量应用负载均衡策略。
func main() {
// configure global configurations and common modules
ins, err := dubbo.NewInstance(
dubbo.WithName("dubbo_test"),
dubbo.WithRegistry(
registry.WithZookeeper(),
registry.WithAddress("127.0.0.1:2181"),
),
)
// configure the params that only client layer cares
cli, err := ins.NewClient()
if err != nil {
panic(err)
}
svc, err := greettriple.NewGreetService(cli)
if err != nil {
panic(err)
}
resp, err := svc.Greet(context.Background(), &greet.GreetRequest{Name: "triple"})
if err != nil {
return err
}
logger.Infof("TRIPLE unary call resp: %s", resp.Greeting)
}
企业级服务治理能力
动态配置
除了 API 模式之外,Dubbo-go 支持基于配置文件驱动的编码方式,这对于一些更大规模的微服务开发场景非常适用。在这种模式下,我们将 registry、protocol 等组件配置,甚至包括服务声明等都放在 dubbogo.yml 文件中,框架会在启动过程中完成配置文件加载。
以下是一个基于 dubbogo.yml 的微服务应用的开发示例:
其中,server.go 定义如下:
func main() {
greettriple.SetProviderService(&GreeterServiceImpl{})
if err := dubbo.Load(); err != nil {
panic(err)
}
}
dubbogo.yml 示例内容如下:
dubbo:
application: # 应用信息,服务启动后会将相关信息注册到注册中心,可被客户端从 url 中识别
name: myApp
registries:
nacos:
protocol: nacos # 注册中心选择 nacos
address: 127.0.0.1:8848 # nacos ip
group: DEFAULT_GROUP # nacos group, 默认 DEFAULT_GROUP
namespace: 9fb00abb-278d-42fc-96bf-e0151601e4a1 # nacos namespaceID, should be created before. 默认public
username: abc
password: abc
protocols:
dubbo:
name: tri
port: 20000
provider:
services:
UserProviderWithCustomGroupAndVersion: # 接口三元组:接口名、版本号、分组。client 和 server 需要保持一致。
interface: org.apache.dubbo.UserProvider.Test # 接口名必填
version: myInterfaceVersion # 默认为空
group: myInterfaceGroup # 默认为空
可以看到相比于之前的 API 编码方式,这里的 server.go 只有两行代码,dubbo.Load() 会完成所有配置的自动组装并启动相关组件,我们只需要在启动应用时指定 export DUBBO_GO_CONFIG_PATH=$ABSOLUTE_PATH/conf/dubbogo.yml 即可。
可观测性
自 3.2.0 版本开始,dubbo-go 重点升级了内置 metrics 指标采集能力,提供 RPC 调用(RT、QPS、调用量、请求成功数、请求失败数、并发请求数等)、注册中心、元数据中心、配置中心交互统计等丰富的内置采集埋点,支持多维度的指标聚合。
dubbo-go 内置 metrics 指标导出到 Prometheus + Grafana 体系的能力,以下是 dubbo-go v3.2.0 示例在 Grafana 的监控效果图,具体示例我们将与随后发布在 dubbo-go-samples/metrics。
流量管控
Dubbo 提供了丰富的流量管控策略:
- 地址发现与负载均衡, 地址发现支持服务实例动态上下线,负载均衡确保流量均匀的分布到每个实例上。
- 基于路由规则的流量管控, 路由规则对每次请求进行条件匹配,并将符合条件的请求路由到特定的地址子集。
服务发现保证调用方看到最新的提供方实例地址,服务发现机制依赖注册中心 (Zookeeper、Nacos、Istio 等) 实现。在消费端,Dubbo 提供了多种负载均衡策略,如随机负载均衡策略、一致性哈希负载、基于权重的轮询、最小活跃度优先、P2C 等。
Dubbo 的流量管控规则可以基于应用、服务、方法、参数等粒度精准的控制流量走向,根据请求的目标服务、方法以及请求体中的其他附加参数进行匹配,符合匹配条件的流量会进一步的按照特定规则转发到一个地址子集。以下是 dubbo-go 流量管控规则可以实现的一些具体管控场景示例:
- 基于权重的比例流量分发
- 灰度验证
- 金丝雀发布
- 按请求参数的路由
- 同区域优先
- 超时时间调整
- 重试
- 限流降级
以下是一个基于 dubbo-go 实现的全链路灰度示例:
以下是一个基于 dubbo-go 实现的按比例流量转发示例:
关于 dubbo-go 流量管控,我们以一个商城系统提供了一个完整的 demo 示例,感兴趣的读者可以参考详细信息:
- 流量管控规则详情 [ 1]
- 流量管控商场示例解读 [ 2]
生态
dubbo-go 总体上遵循框架内核+插件的的设计理念,左侧的框架内核定义了 dubbo-go 作为微服务框架的一些核心概念,右侧的插件部分则提供了核心概念扩展实现。
框架内核 可分为 4 个层次,从上到下依次为:
-
API 层
dubbo-go 同时支持基于 IDL、interface/struct 的服务契约定义,兼顾跨语言与易用性诉求;支持基于纯 yaml 文件的微服务配置模式;提供了同步、异步、单次(unary)、流式(streaming) 等 RPC 通信与编码模型。
-
服务治理层
dubbo-go 内置了多维度的服务治理能力抽象,确保满足微服务开发与集群治理的核心诉求,这包括地址发现(Service Discovery)、负载均衡(Load Balancing)、可观测指标(Metrics)、流量管控(Traffic Management)、全链路追踪(Tracing)等。
-
RPC 协议层
dubbo-go 实现的最核心的 RPC 协议是 - triple 协议,triple 可同时工作在 http1/2 之上 (支持 CURL 直接访问),兼容 gRPC;从设计上,dubbo-go 还提供了多协议发布服务的支持,你可以在一个进程内同时发布 triple、dubbo2、rest、jsonRPC 等多种不同通信协议的服务。
-
传输层支持 HTTP1/2、TCP 传输层,兼顾性能与通用性,同时支持多种序列化方式。
插件体系极大的丰富了 dubbo-go 功能与生态,社区内置提供了大量的内置扩展实现,同时,开发者可以非常容易的根据需求增加扩展实现。以下是一些典型的插件定义:
-
Protocol
dubbo-go 基于 protocol 插件内置提供了 triple、dubbo2、rest 等协议支持,通过扩展 protocol 可以为 dubbo-go 扩展更多协议。
-
Service Discovery
支持 Nacos、Zookeeper、Polaris 等主流注册中心集成。
-
Traffic Management
dubbo-go 支持 Dubbo 体系定义的流量规则,可以实现在运行期动态的调整服务行为如超时时间、重试次数、限流参数等,通过控制流量分布可以实现 A/B 测试、金丝雀发布、多版本按比例流量分配、条件匹配路由、黑白名单等。
-
Metrics
提供 RPC 调用(RT、QPS、调用量、请求成功数、请求失败数、并发请求数等)、注册中心、元数据中心、配置中心交互统计等丰富的内置采集埋点,支持多维度的指标聚合。
-
Logging
提供通用的日志采集接口定义,内置 Zap、Logrus 支持。
-
Tracing
提供分布式链路追踪能力,通过此插件扩展可接入 Zipkin、Jaeger、Skywalking 等链路追踪系统。
总结
dubbo-go 3.2.0 的首个 alpha 版本将于 11 月底发布,本文是发版前的抢先预览,感兴趣的读者也可以访问源码尝鲜:https://github.com/apache/dubbo-go/tree/feature-triple/protocol/triple/internal
接下来,我们将持续推进 3.2.0 版本迭代并计划与 2 月份发布正式稳定版本,详细 Roadmap 请关注项目仓库:https://github.com/apache/dubbo-go
社区期待 Go 语言相关开发者的加入,可搜索关注 “apachedubbo” 微信公众号并回复 “dubbogo” 接受邀请,加入官方社群组织。
相关链接:
[1] 流量管控规则详情
https://cn.dubbo.apache.org/zh-cn/overview/core-features/traffic/
[2] 流量管控商场示例解读
https://cn.dubbo.apache.org/zh-cn/overview/tasks/traffic-management/