文章目录
- [1. Triple 协议](#1. Triple 协议)
-
- [1.1 介绍](#1.1 介绍)
- [1.2 简单示例](#1.2 简单示例)
- [2. 注册中心](#2. 注册中心)
- [3. 小结](#3. 小结)
本系列文章:
- Java 转 go 学习 - 项目管理
- Java 转 go 学习 - 基本语法
- Java 转 go 学习 - 类型转换
- Java 转 go 学习 - 流程控制结构
- Java 转 go 学习 - 数组和切片
- Java 转 go 学习 - map
- Java 转 go 学习 - 函数(1)
- Java 转 go 学习 - 函数(2)
- Java 转 go 学习 - 结构体
- Java 转 go 学习 - 接口
- Java 转 go 学习 - 并发编程(1)
- Java 转 go 学习 - 并发编程(2)
- Java 转 go 学习 - 并发编程(3)
- Java 转 go 学习 - web 编程
- Java 转 go 学习 - Hertz 学习(1)
- Java 转 go 学习 - Hertz 学习(2)
- Java 转 go 学习 - Redis(1)
- Java 转 go 学习 - Redis(2)
- Java 转 go 学习 - MYSQL
- Java 转 go 学习 - GRPC(1)
- Java 转 go 学习 - GRPC(2)
- Java 转 go 学习 - GRPC(3)
- Java 转 go 学习 - GRPC(4)
- Java 转 go 学习 - kitex(1)
- Java 转 go 学习 - kitex(2)
- Java 转 go 学习 - kitex(3)
- Java 转 go 学习 - kitex(4)
- Java 转 go 学习 - kitex(5)
1. Triple 协议
1.1 介绍
从这篇文章开始学习 dubbo 协议相关的内容,其实主要学的是 Go 调用 Java 提供的 dubbo 接口,因为工作中需要用到这个。
Triple 是 Dubbo3 主推的云原生通信协议,基于 HTTP/2 且100% 兼容 gRPC,同时支持 HTTP/1.1 降级,可通过 curl / 浏览器直接访问,是 Dubbo-go 跨语言互通的首选协议。
Triple 基于 Protobuf 定义服务,所以天然就跨语言,跟 gRpc 一样支持四种 RPC 模式:Unary 单请求响应 、Server Stream 、Client Stream 、Bi-Stream ,但是学的话还是以第一种一元 RPC 为主。本质上 Triple 就是 Dubbo 魔改版的增强 gRPC ,写法跟标准 gRPC 一模一样,就是 proto 生成代码的时候带上 go-triple_out 指定 triple.go 文件的输出路径。
1.2 简单示例
示例非常简单,首先我们定义服务端提供 Greet 接口给消费者调用,还是一样先编写 greet.proto 文件。
go
syntax = "proto3";
package greet;
option go_package = "example.com/dubbo-1/proto;greet";
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
接下来使用下面的命令生成代码。
powershell
protoc --go_out=. --go_opt=paths=source_relative `
--go-triple_out=. --go-triple_opt=paths=source_relative `
.\proto\greet.proto
接下来看生产者 provider 的代码。
go
package main
import (
"context"
"log"
_ "dubbo.apache.org/dubbo-go/v3/imports"
greet "example.com/dubbo-1/proto"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/server"
)
type GreetTripleServer struct{}
func (s *GreetTripleServer) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
return &greet.GreetResponse{
Greeting: "hello, " + req.Name,
}, nil
}
func main() {
srv, err := server.NewServer(
// 设置服务端协议
server.WithServerProtocol(
// 设置 dubbo 端口
protocol.WithPort(20000),
// 配置 triple 协议
protocol.WithTriple(),
),
)
if err != nil {
log.Fatal(err)
}
// 调用生成的 triple.go 里面的 RegisterGreetServiceHandler 方法注册接口实现
if err := greet.RegisterGreetServiceHandler(srv, &GreetTripleServer{}); err != nil {
log.Fatal(err)
}
log.Println("dubbo-go provider listening on :20000")
if err := srv.Serve(); err != nil {
log.Fatal(err)
}
}
理解下,server.NewServer 是 dubbo-go 包里面提供的方法,用于启动 dubbo 服务,在里面指定 dubbo 监听的端口和协议,指定协议是因为这个框架不单单支持 triple 协议,还支持其他的,所以指定下。
后面通过 greet.RegisterGreetServiceHandler 将实现接口给注册上去,就能提供给消费者使用了,消费者代码如下。
go
package main
import (
"context"
"log"
_ "dubbo.apache.org/dubbo-go/v3/imports"
greet "example.com/dubbo-1/proto"
"dubbo.apache.org/dubbo-go/v3/client"
)
func main() {
cli, err := client.NewClient(
client.WithClientURL("127.0.0.1:20000"),
)
if err != nil {
log.Fatal(err)
}
// 创建客户端
svc, err := greet.NewGreetService(cli)
if err != nil {
log.Fatal(err)
}
// 调用方法
resp, err := svc.Greet(context.Background(), &greet.GreetRequest{
Name: "dubbo-go",
})
if err != nil {
log.Fatal(err)
}
log.Printf("greet response: %s", resp.Greeting)
}
要注意得加上这行:_ "dubbo.apache.org/dubbo-go/v3/imports",dubbo-go 这套 SPI/扩展机制不是 自动全注册 的,虽然我们配置了协议 protocol.WithTriple(),但是对应的协议实现、proxy factory、metadata exporter 等扩展没有通过匿名导入注册进来,所以启动时会去拿默认扩展,没有显示 import 的情况下获取到 nil 就会报错了。
下面启动 provider,输出如下,可以看到暴露出去的方法协议是 tri://:20000。
go
2026-05-03 16:38:49 INFO server/server.go:111 Registering service: GreetTripleServer
2026-05-03 16:38:49 INFO server/server.go:362 A provider service GreetTripleServer was registered successfully.
2026-05-03 16:38:49 INFO dubbo/dubbo_protocol.go:85 [DUBBO Protocol] Export service: dubbo://:28224/org.apache.dubbo.metadata.MetadataService?bean.name=MetadataService&group=dubbo.io&interface=org.apache.dubbo.metadata.MetadataService&metadata-type=local&release=3.3.1&serialization=hessian2&side=provider&version=1.0.0
2026-05-03 16:38:49 INFO triple/triple.go:68 [TRIPLE Protocol] Export service: tri://:20000/greet.GreetService?IDL-mode=IDL&accesslog=&app.version=&application=dubbo.io&application.tag=&auth=&bean.name=GreetTripleServer&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=greet.GreetService&loadbalance=random&max-server-recv-msg-size=&max-server-send-msg-size=&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go¶m.sign=&pid=21176®istry.role=3&release=dubbo-golang-3.3.1&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider×tamp=1777797529&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&warmup=&weight=0
2026-05-03 16:38:49 INFO server/server.go:111 Registering service: HealthCheckServer
2026-05-03 16:38:49 INFO server/server.go:111 Registering service: ReflectionServer
2026-05-03 16:38:49 INFO triple/triple.go:68 [TRIPLE Protocol] Export service: tri://:20000/grpc.health.v1.Health?IDL-mode=IDL&accesslog=&app.version=&application=dubbo.io&application.tag=&auth=&bean.name=HealthCheckServer&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=grpc.health.v1.Health&loadbalance=random&max-server-recv-msg-size=&max-server-send-msg-size=&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go¶m.sign=&pid=21176®istry.role=3&release=dubbo-golang-3.3.1&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider×tamp=1777797529&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&warmup=&weight=0
2026-05-03 16:38:49 INFO triple/triple.go:68 [TRIPLE Protocol] Export service: tri://:20000/grpc.reflection.v1alpha.ServerReflection?IDL-mode=IDL&accesslog=&app.version=&application=dubbo.io&application.tag=&auth=&bean.name=ReflectionServer&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=grpc.reflection.v1alpha.ServerReflection&loadbalance=random&max-server-recv-msg-size=&max-server-send-msg-size=&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go¶m.sign=&pid=21176®istry.role=3&release=dubbo-golang-3.3.1&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider×tamp=1777797529&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&warmup=&weight=0
2026/05/03 16:38:49 dubbo-go provider listening on :20000
然后启动消费者去消费。
go
2026-05-03 16:38:51 INFO client/action.go:61 URL specified explicitly 127.0.0.1:20000
2026-05-03 16:38:51 INFO triple/triple.go:127 [TRIPLE Protocol] Refer service: tri://127.0.0.1:20000/greet.GreetService?IDL-mode=&app.version=&application=dubbo.io&async=false&bean.name=greet.GreetService&cluster=failover&config.tracing=&environment=&generic=&group=&interface=greet.GreetService&keep-alive-interval=&keep-alive-timeout=&loadbalance=&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&peer=true&provided-by=&reference.filter=cshutdown®istry.role=0&release=dubbo-golang-3.3.1&remote.timestamp=&retries=&serialization=protobuf&side=consumer&sticky=false&timeout=3s×tamp=1777797531&version=
2026/05/03 16:38:51 greet response: hello, dubbo-go
2. 注册中心
上面的示例注册和消费都是直连,没有用到注册中心,消费者是直接连上 provider 去消费的,生产环境下肯定是需要注册中心的,所以这里来演示下加上注册中心的示例。
首先还是用 triple 协议,且 greet.proto 文件不变,我们配置用的是 yaml,首先来看下 provider 的配置,其实配置跟 Java 的 xml 配置差不多的,只是字段设置不一样。
yaml
# dubbo-go 的所有核心配置都放在 dubbo 节点下。
dubbo:
# application 描述当前进程自己的身份信息, 这几个配置跟调用谁没有关系, 只是说会出现在注册信息和运行日志里
application:
name: dubbo-1-provider
module: greet-provider
version: 1.0.0
# 用的是 Triple 协议, 对应 dubbo-go 里的 tri 协议名
protocols:
triple:
# name 是协议名, Triple 协议这里要写 tri, 不能随便乱起
name: tri
# ip 和 port 共同决定 provider 真正监听的地址
ip: 127.0.0.1
port: 20000
# registries 表示注册中心
registries:
# 这里只配置一个 zk, zk 是注册中心的 id, 后面 provider 和 consumer 都通过 registry-id 去引用
zk:
# protocol 说明这是 zookeeper 注册中心。
protocol: zookeeper
timeout: 3s
# address 是 zookeeper 地址,不要带协议前缀。
address: 127.0.0.1:2181
provider:
# 注册到注册中心
register: true
# 注册中心的配置, 可以配置多个
registry-ids:
- zk
# 需要导出的服务
services:
# greetService 只是这条配置在 YAML 里的名字, 真正的接口名看 interface 字段
greetService:
# interface 必须和生成代码里的 InterfaceName 对上, 我们这个示例的生成值是 greet.GreetService, 具体看 greet.triple.go
interface: greet.GreetService
# protocol 指定这个服务走哪个协议, 对应上面 protocols 里的 key/name
protocol: tri
# 这里可以单独配置 registry-ids, 也就是说服务级配置可以覆盖或细化 provider 级默认配置
registry-ids:
- zk
里面都有比较详细的注释,这里就不多说了,我们来看下注册到 zookeeper 是什么样的,用的工具是 prettyZoo。

服务详细信息如下。
json
{
"name" : "dubbo-1-provider",
"id" : "127.0.0.1:20000",
"address" : "127.0.0.1",
"port" : 20000,
"payload" : {
"@class" : "org.apache.dubbo.registry.zookeeper.ZookeeperInstance",
"id" : "127.0.0.1:20000",
"metadata" : {
"dubbo.endpoints" : "[{\"port\":20000,\"protocol\":\"tri\"}]",
"dubbo.metadata-service.url-params" : "{\"group\":\"dubbo-1-provider\",\"port\":\"43726\",\"protocol\":\"dubbo\",\"release\":\"3.3.1\",\"serialization\":\"hessian2\",\"version\":\"1.0.0\"}",
"dubbo.metadata.revision" : "5336813594",
"dubbo.metadata.storage-type" : "local",
"dubbo.subscribed-services.revision" : "0",
"meta-v" : "1.0.0"
},
"name" : "dubbo-1-provider"
}
}
然后就是消费者,消费者跟生产者也是差不多的。
go
# consumer 这边同样把配置统一放在 dubbo 节点下
dubbo:
# application 描述当前 consumer 进程自己的身份
application:
name: dubbo-1-consumer
module: greet-consumer
version: 1.0.0
# 注册中心, 去服务发现
registries:
zk:
protocol: zookeeper
timeout: 3s
address: 127.0.0.1:2181
# consumer 描述默认怎么引用远程服务。
consumer:
# check=true 表示启动时就检查服务是否可用
# 如果注册中心里完全没有对应 provider, 启动/加载阶段就可能直接报错
check: true
# 默认从哪些 registry 里找服务, 对应上面 registries 里面的 zk
registry-ids:
- zk
# references 是当前 consumer 需要引用的远程服务列表
# greetService 是这条引用配置在 YAML 里的名字
references:
greetService:
# interface 必须和 provider 导出的接口名一致
interface: greet.GreetService
# protocol 表示按 Triple 方式去调用这个服务
protocol: tri
# 这条引用具体走哪个注册中心找 provider, 可以用于覆盖上面的 registries 里面的配置
registry-ids:
- zk
最后来看下 server 提供者 main 方法。
go
package main
import (
"context"
"log"
"time"
dubbo "dubbo.apache.org/dubbo-go/v3"
_ "dubbo.apache.org/dubbo-go/v3/imports"
greet "example.com/dubbo-1/proto"
)
type GreetTripleServer struct{}
func (s *GreetTripleServer) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
return &greet.GreetResponse{
Greeting: "hello, " + req.Name,
}, nil
}
func main() {
// 先把本地服务实现登记给 dubbo-go,再由 YAML 决定协议和注册中心。
greet.SetProviderGreetService(&GreetTripleServer{})
if err := dubbo.Load(dubbo.WithPath("conf/provider.yaml")); err != nil {
log.Fatal(err)
}
log.Println("dubbo-go provider started with registry zookeeper://127.0.0.1:2181")
for {
time.Sleep(time.Hour)
}
}
dubbo-go 比较好的一点就是不用我们自己去解析文件,把 yaml 文件的路径配置上,框架就能帮我们自动去解析,比较方便。
注意到上面在解析文件之前有一行是 greet.SetProviderGreetService(&GreetTripleServer{}),这一行的作用就是告诉 dubbo-go 框架,这个接口的实现是什么,我们进入源码来看下这个方法。
go
// SetProviderServiceWithInfo sets the provider service with the server information.
func SetProviderServiceWithInfo(svc common.RPCService, info *common.ServiceInfo) {
proLock.Lock()
defer proLock.Unlock()
providerServices[info.InterfaceName] = &server.ServiceDefinition{
Handler: svc,
Info: info,
}
}
可以看出这个方法就是把 GreetTripleServer 设置到了 Handler 上面,看 Handler 大概能猜出处理流程跟 gRPC 差不多,收到请求之后通过 info.InterfaceName 找到 Handler,再调用具体 Handler 去处理对应的请求。
上面 1.2 小节的例子不需要设置这个,是因为这个例子属于直连,没有用到注册中心,我们调用 greet.RegisterGreetServiceHandler 就已经注册了,本质上是下面三行代码。
go
srv, _ := server.NewServer(...)
greet.RegisterGreetServiceHandler(srv, &GreetTripleServer{})
srv.Serve()
用了注册中心之后上面三步框架帮我们去处理,所以我们当然就要告诉框架有哪些 provider 服务要导出,这些服务的实现类是什么,因此就需要 SetProviderServiceWithInfo 去告诉框架实现是什么。
好了,继续看 Consumer 的启动代码。
go
package main
import (
"context"
"log"
dubbo "dubbo.apache.org/dubbo-go/v3"
_ "dubbo.apache.org/dubbo-go/v3/imports"
greet "example.com/dubbo-1/proto"
)
func main() {
// 先登记 consumer 引用对象,再由 YAML 决定从哪个注册中心发现服务。
svc := &greet.GreetServiceImpl{}
greet.SetConsumerGreetService(svc)
if err := dubbo.Load(dubbo.WithPath("conf/consumer.yaml")); err != nil {
log.Fatal(err)
}
resp, err := svc.Greet(context.Background(), &greet.GreetRequest{
Name: "dubbo-go",
})
if err != nil {
log.Fatal(err)
}
log.Printf("greet response: %s", resp.Greeting)
}
最后来看下启动调用情况,生产者的上面已经展示了,就是 zookeeper 注册的这些信息,所以直接看消费者的就行。
go
...
2026-05-03 19:01:12 INFO server/server.go:111 Registering service: HealthCheckServer
2026-05-03 19:01:12 INFO server/server.go:111 Registering service: ReflectionServer
2026-05-03 19:01:12 WARN server/action.go:150 The service grpc.health.v1.Health'svcOpts '[]' protocols don't has right protocolConfigs, Please check your configuration center and transfer protocol
2026-05-03 19:01:12 WARN server/action.go:150 The service grpc.reflection.v1alpha.ServerReflection'svcOpts '[]' protocols don't has right protocolConfigs, Please check your configuration center and transfer protocol
2026/05/03 19:01:12 greet response: hello, dubbo-go
3. 小结
好了,关于 dubbo 的例子就先学习到这里,下一篇文章来看下 dubbo-go 协议。
如有错误,欢迎指出!!!