Go语言微服务框架重磅升级:dubbo-go v3.2.0 -alpha 版本预览

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 随着 Dubbo3 在云原生微服务方向的快速发展,Dubbo 的 go 语言实现迎来了 Dubbo3 版本以来最全面、最大幅度的一次升级,这次升级是全方位的,涉及 API、协议、流量管控、可观测能力等。

作者:王宇轩, 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 功能与生态,社区内置提供了大量的内置扩展实现,同时,开发者可以非常容易的根据需求增加扩展实现。以下是一些典型的插件定义:

  • Protocoldubbo-go 基于 protocol 插件内置提供了 triple、dubbo2、rest 等协议支持,通过扩展 protocol 可以为 dubbo-go 扩展更多协议。
  • Service Discovery支持 Nacos、Zookeeper、Polaris 等主流注册中心集成。
  • Traffic Managementdubbo-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” 接受邀请,加入官方社群组织。


电子书免费下载本书为 CoC Asia 2023(原ApacheCon) RPC & Service Mesh 专题分享实录,通过摘取 Community Over Code Asia 2023(原 ApacheCon Asia)的 RPC & Service Mesh专题的精彩内容,我们以 Apache Dubbo 为中心展开,给大家带来开源微服务技术方向发展、云原生微服务选型、企业实践分享的精彩内容!


相关链接:

[1] 流量管控规则详情

https://cn.dubbo.apache.org/zh-cn/overview/core-features/traffic/

[2] 流量管控商场示例解读

https://cn.dubbo.apache.org/zh-cn/overview/tasks/traffic-management/

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
3天前
|
人工智能 Go 调度
掌握Go并发:Go语言并发编程深度解析
掌握Go并发:Go语言并发编程深度解析
|
6天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
2天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
7 1
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello("Alice")`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
13 1
|
3天前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
3天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
11 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
|
3天前
|
Go
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
【4月更文挑战第21天】Go语言函数是代码组织的基本单元,用于封装可重用逻辑。本文介绍了函数定义(包括基本形式、命名、参数列表和多返回值)、调用以及匿名函数与闭包。在函数定义时,注意参数命名和注释,避免参数顺序混淆。在调用时,要检查并处理多返回值中的错误。理解闭包原理,小心处理外部变量引用,以提升代码质量和可维护性。通过实践和示例,能更好地掌握Go语言函数。
18 1
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
|
4天前
|
程序员 Go API
【Go语言快速上手(二)】 分支与循环&函数讲解
【Go语言快速上手(二)】 分支与循环&函数讲解
|
5天前
|
Go
Golang深入浅出之-Go语言基础语法:变量声明与赋值
【4月更文挑战第20天】本文介绍了Go语言中变量声明与赋值的基础知识,包括使用`var`关键字和简短声明`:=`的方式,以及多变量声明与赋值。强调了变量作用域、遮蔽、初始化与零值的重要性,并提醒读者注意类型推断时的一致性。了解这些概念有助于避免常见错误,提高编程技能和面试表现。
19 0
|
5天前
|
编译器 Go 开发者
Go语言入门|包、关键字和标识符
Go语言入门|包、关键字和标识符
22 0