作者:王宇轩, Apache Dubbo Committer
随着 Dubbo3 在云原生微服务方向的快速发展,Dubbo 的 go 语言实现迎来了 Dubbo3 版本以来最全面、最大幅度的一次升级,这次升级是全方位的,涉及 API、协议、流量管控、可观测能力等。
总的来说,新版本的 dubbo-go:
基于 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 协议,我们接下来会尝试启动一个 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)
}
}
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 服务的测试验证、前端接入成本。
Dubbo Go SDK 支持使用 IDL 或编程语言特有的方式定义服务,并提供一套轻量的 API 来发布或调用这些服务。在上一节的示例中,我们已经看到了部分 dubbo-go API 的使用方式,接下来,让我们更仔细全面的看一下新版本的 API 设计。
对于一些 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 示例,感兴趣的读者可以参考详细信息:
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/