Java 转 go 学习 - dubbo-go(1)

文章目录

  • [1. Triple 协议](#1. Triple 协议)
    • [1.1 介绍](#1.1 介绍)
    • [1.2 简单示例](#1.2 简单示例)
  • [2. 注册中心](#2. 注册中心)
  • [3. 小结](#3. 小结)

本系列文章:


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&param.sign=&pid=21176&registry.role=3&release=dubbo-golang-3.3.1&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&timestamp=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&param.sign=&pid=21176&registry.role=3&release=dubbo-golang-3.3.1&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&timestamp=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&param.sign=&pid=21176&registry.role=3&release=dubbo-golang-3.3.1&retries=&serialization=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&timestamp=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&registry.role=0&release=dubbo-golang-3.3.1&remote.timestamp=&retries=&serialization=protobuf&side=consumer&sticky=false&timeout=3s&timestamp=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 协议。

如有错误,欢迎指出!!!

相关推荐
磁场转动100万匹3 小时前
Hive 学习第三天:HQL 查询精讲与内置函数实战
hive·hadoop·学习
JWASX3 小时前
go 学习 - prometheus 指标学习
学习·golang·prometheus
脉动数据行情13 小时前
Go 语言实战|对接美股英伟达 NVDA 实时行情 生产级 WebSocket 方案
开发语言·websocket·golang
小弥儿3 小时前
GitHub今日热榜 | 2026-10-08:逆向工程 MCP 登顶
学习·开源·github
代码山河3 小时前
JDK、JRE、JVM的区别:一文讲清楚Java运行环境
java·学习·架构·教程·面向对象·项目
fj800j4 小时前
首助记账本新增「标签管理」功能:跨分类归集项目账目,一个账本管完所有事
数据结构·经验分享·笔记·学习·分布式账本
JWASX4 小时前
Java 转 go 学习 - dubbo-go(2)
学习·golang
陈年老古董4 小时前
Hive DML 语言学习笔记:数据加载、插入、导出与导入
hive·笔记·学习·mysql
kkkkkkkkkk_Z4 小时前
新手自学嵌入式 | 学习日记:ARM内核启动代码、时钟系统(PLL/Prescaler/PFD)与IMX6ULL时钟配置
arm开发·学习