文章目录
- [1. dubbo 协议](#1. dubbo 协议)
-
- [1.1 证书](#1.1 证书)
- [1.2 配置文件](#1.2 配置文件)
- [1.3 接口定义](#1.3 接口定义)
- [1.4 服务端](#1.4 服务端)
- [1.5 客户端启动](#1.5 客户端启动)
- [1.6 示例](#1.6 示例)
- [2. go 调用 java dubbo 接口](#2. go 调用 java dubbo 接口)
-
- [2.1 Java Dubbo 接口提供](#2.1 Java Dubbo 接口提供)
-
- [2.1.1 bean](#2.1.1 bean)
- [2.1.2 接口](#2.1.2 接口)
- [2.1.3 配置文件](#2.1.3 配置文件)
- [2.2 go 代码调用](#2.2 go 代码调用)
-
- [2.2.1 接口提供](#2.2.1 接口提供)
- [2.2.2 配置文件](#2.2.2 配置文件)
- [2.2.3 consumer main 方法](#2.2.3 consumer main 方法)
- [2.2.4 启动输出](#2.2.4 启动输出)
- [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)
- Java 转 go 学习 - dubbo-go(1)
1. dubbo 协议
官方地址:Dubbo-go 协议快速开始,有需要可以直接看官方给的示例,还是比较详细的,我这里就是用 codex 生成的一个示例。
1.1 证书
首先就是协议证书,目前这个版本是 v3.3.1,但是这个版本好像都有个 bug,使用默认的协议就一定要配置 tls,如果不配置就会报错,具体看这个链接。
看评论在 3.3.2 版本已经修复了,但是这个版本目前还引用不了,所以本地用一个脚本去生成 tls 证书。
go
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"net"
"os"
"path/filepath"
"time"
)
func main() {
baseDir := filepath.Join("certs")
must(os.MkdirAll(baseDir, 0o755))
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
must(err)
caTemplate := &x509.Certificate{
SerialNumber: serial(),
Subject: pkix.Name{CommonName: "dubbo-4 local ca"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().AddDate(5, 0, 0),
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
IsCA: true,
}
caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey)
must(err)
writeCert(filepath.Join(baseDir, "ca.crt"), caDER)
writeKey(filepath.Join(baseDir, "ca.key"), caKey)
serverKey, serverCert := issueLeaf(caTemplate, caKey, "dubbo-4-server", []string{"localhost"}, []net.IP{net.ParseIP("127.0.0.1")}, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
writeKey(filepath.Join(baseDir, "server.key"), serverKey)
writeCert(filepath.Join(baseDir, "server.crt"), serverCert)
clientKey, clientCert := issueLeaf(caTemplate, caKey, "dubbo-4-client", nil, nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth})
writeKey(filepath.Join(baseDir, "client.key"), clientKey)
writeCert(filepath.Join(baseDir, "client.crt"), clientCert)
fmt.Println("generated certificates:")
fmt.Println(filepath.Join(baseDir, "ca.crt"))
fmt.Println(filepath.Join(baseDir, "ca.key"))
fmt.Println(filepath.Join(baseDir, "server.crt"))
fmt.Println(filepath.Join(baseDir, "server.key"))
fmt.Println(filepath.Join(baseDir, "client.crt"))
fmt.Println(filepath.Join(baseDir, "client.key"))
}
func issueLeaf(ca *x509.Certificate, caKey *rsa.PrivateKey, cn string, dns []string, ips []net.IP, usage []x509.ExtKeyUsage) (*rsa.PrivateKey, []byte) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
must(err)
template := &x509.Certificate{
SerialNumber: serial(),
Subject: pkix.Name{CommonName: cn},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().AddDate(2, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: usage,
DNSNames: dns,
IPAddresses: ips,
}
der, err := x509.CreateCertificate(rand.Reader, template, ca, &key.PublicKey, caKey)
must(err)
return key, der
}
func writeCert(path string, der []byte) {
file, err := os.Create(path)
must(err)
defer file.Close()
must(pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: der}))
}
func writeKey(path string, key *rsa.PrivateKey) {
file, err := os.Create(path)
must(err)
defer file.Close()
must(pem.Encode(file, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}))
}
func serial() *big.Int {
n, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
must(err)
return n
}
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
1.2 配置文件
接下来是 provider.yaml,生产者的配置信息。
go
dubbo:
application:
name: dubbo-4-provider
module: dubbo-protocol-provider
version: 1.0.0
# 这里配置 provider 自己的 TLS 证书
# dubbo-go/getty 这一版会从全局 tls_config 里读取证书文件路径
tls_config:
# 服务端自己的证书链文件
tls-cert-file: certs/server.crt
# 服务端自己的私钥文件
tls-key-file: certs/server.key
# 用来校验对端客户端证书的 CA
ca-cert-file: certs/ca.crt
# 证书里的服务名,这里本地自签用 localhost
tls-server-name: localhost
protocols:
dubbo:
name: dubbo
ip: 127.0.0.1
port: 20000
registries:
zk:
protocol: zookeeper
timeout: 3s
address: 127.0.0.1:2181
provider:
register: true
registry-ids:
- zk
services:
# 服务
com.example.dubbo4.HelloService:
interface: com.example.dubbo4.HelloService
protocol: dubbo
group: dubbo-4
version: 1.0.0
registry-ids:
- zk
还有消费者的 consumer.yaml 如下。
go
# 这是传统 dubbo 协议的 consumer 配置。
dubbo:
application:
name: dubbo-4-consumer
module: dubbo-protocol-consumer
version: 1.0.0
# 这里配置 consumer 侧的 TLS。
# consumer 要带自己的客户端证书,同时要信任 provider 使用的 CA。
tls_config:
tls-cert-file: certs/client.crt
tls-key-file: certs/client.key
ca-cert-file: certs/ca.crt
tls-server-name: localhost
registries:
zk:
protocol: zookeeper
timeout: 3s
address: 127.0.0.1:2181
consumer:
check: true
request-timeout: 3s
registry-ids:
- zk
references:
com.example.dubbo4.HelloService:
interface: com.example.dubbo4.HelloService
protocol: dubbo
group: dubbo-4
version: 1.0.0
registry-ids:
- zk
timeout: 2s
1.3 接口定义
下面是接口定义的代码。
go
package service
import (
"context"
hessian "github.com/apache/dubbo-go-hessian2"
)
func init() {
// 传统 dubbo 协议走 Hessian2, 只要方法里传输自定义结构体,就需要先把 POJO 注册给 hessian 库。
hessian.RegisterPOJO(&User{})
}
// User 是示例里跨 consumer/provider 传输的自定义结构体。
// 这种 POJO 在 dubbo 协议下需要实现 JavaClassName。
type User struct {
Name string `hessian:"name"`
Age int32 `hessian:"age"`
Email string `hessian:"email"`
}
// JavaClassName 返回这个 Go 结构体在 Java 世界里的类名。
func (u *User) JavaClassName() string {
return "com.example.dubbo4.User"
}
// HelloService 是 dubbo 协议示例里导出和引用的接口。
// 传统 dubbo-go 这条线不走 proto 生成代码,而是直接靠接口名 + Hessian2 序列化。
type HelloService struct{}
// Reference 返回 consumer 侧要匹配的接口名。
func (s *HelloService) Reference() string {
return "com.example.dubbo4.HelloService"
}
// JavaClassName 返回服务接口在 Java 侧看到的类名。
func (s *HelloService) JavaClassName() string {
return "com.example.dubbo4.HelloService"
}
// UpdateUser 演示 POJO 同时作为入参和返回值。
func (s *HelloService) UpdateUser(ctx context.Context, user *User) (*User, error) {
return &User{
Name: user.Name,
Age: user.Age + 1,
Email: "updated+" + user.Email,
}, nil
}
上面有几个关键的要点:
hessian.RegisterPOJO(&User{})注册结构体,因为 dubbo 协议这条线底层用的是 Hessian2,而 Hessian2 在处理 自定义对象 的时候,必须知道这是什么类。当 consumer 把 User 发出去的时候,协议里不只是传字段值,还要带这个对象的类型信息,同理 provider 收到请求之后也不能只拿到一个普通 map 去接收参数,它需要知道应该把数据还原成哪个 Go 结构体。JavaClassName配置这个 Go 结构体在 Java 里的类名,如果要调用 java 提供的 dubbo 方法,就需要配置这个。Reference方法返回 consumer 侧要匹配的接口名。
1.4 服务端
go
package main
import (
"log"
"time"
dubbo "dubbo.apache.org/dubbo-go/v3"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"example.com/dubbo-4/service"
)
func main() {
// 配置化启动时,先把本地 Go 服务实现登记给 dubbo-go。
dubbo.SetProviderService(&service.HelloService{})
if err := dubbo.Load(dubbo.WithPath("conf/provider.yaml")); err != nil {
log.Fatal(err)
}
log.Println("dubbo-4 provider started with dubbo protocol")
for {
time.Sleep(time.Hour)
}
}
服务端就很简单了,先把本地 Go 服务实现登记给 dubbo-go,然后解析配置文件即可。
1.5 客户端启动
客户端也是,同理。
go
package main
import (
"context"
"log"
dubbo "dubbo.apache.org/dubbo-go/v3"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"example.com/dubbo-4/service"
)
func main() {
svc := new(service.HelloService)
dubbo.SetConsumerService(svc)
if err := dubbo.Load(dubbo.WithPath("conf/consumer.yaml")); err != nil {
log.Fatal(err)
}
resp, err := svc.UpdateUser(context.Background(), &service.User{
Name: "dubbo-go",
Age: 20,
Email: "dubbo-go@example.com",
})
if err != nil {
log.Fatal(err)
}
log.Printf("updateUser response: name=%s age=%d email=%s", resp.Name, resp.Age, resp.Email)
}
1.6 示例
启动服务端和客户端,输出分别如下。


2. go 调用 java dubbo 接口
上面的示例都是 go 调用 go 的接口,在 1.3 接口定义我们也看到了通过 JavaClassName 配置 Java 的对象映射,所以这一小节就来看下 go 是如何调用 Java 的 dubbo 接口,首先所用的依赖版本如下。
go
require (
github.com/apache/dubbo-go v1.5.8
github.com/apache/dubbo-go-hessian2 v1.9.4
)
2.1 Java Dubbo 接口提供
项目结构如下。

2.1.1 bean
首先是几个 bean,Address。
java
package com.demo.bean;
import java.io.Serializable;
public class Address implements Serializable {
private String city;
private String street;
private String zipCode;
// 无参构造(Hessian序列化必须)
public Address() {}
// Getter & Setter
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getZipCode() { return zipCode; }
public void setZipCode(String zipCode) { this.zipCode = zipCode; }
}
Order.Java。
java
package com.demo.bean;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class Order implements Serializable {
private String orderNo;
private User buyer;
private List<OrderItem> items;
private Map<String, String> tags;
public Order() {}
// Getter & Setter
public String getOrderNo() { return orderNo; }
public void setOrderNo(String orderNo) { this.orderNo = orderNo; }
public User getBuyer() { return buyer; }
public void setBuyer(User buyer) { this.buyer = buyer; }
public List<OrderItem> getItems() { return items; }
public void setItems(List<OrderItem> items) { this.items = items; }
public Map<String, String> getTags() { return tags; }
public void setTags(Map<String, String> tags) { this.tags = tags; }
}
OrderItem.java
java
package com.demo.bean;
import java.io.Serializable;
public class OrderItem implements Serializable {
private String sku;
private Integer quantity;
private Long price;
public OrderItem() {}
// Getter & Setter
public String getSku() { return sku; }
public void setSku(String sku) { this.sku = sku; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public Long getPrice() { return price; }
public void setPrice(Long price) { this.price = price; }
}
User.java。
go
package com.demo.bean;
import java.io.Serializable;
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
private String email;
private Address address;
public User() {}
// Getter & Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
}
2.1.2 接口
然后下面是 service 接口和实现类。
go
package com.demo.service;
import com.demo.bean.Order;
import com.demo.bean.User;
import java.util.List;
import java.util.Map;
public interface UserProfileService {
String echoBasic(long id, String name, boolean active);
Order mirrorOrder(Order order);
List<User> mirrorUsers(List<User> users);
Map<String, String> mirrorAttributes(Map<String, String> attrs);
}
java
package com.demo.service.impl;
import com.demo.bean.Order;
import com.demo.bean.User;
import com.demo.service.UserProfileService;
import org.apache.dubbo.config.annotation.DubboService;
import java.util.List;
import java.util.Map;
public class UserProfileServiceImpl implements UserProfileService {
@Override
public String echoBasic(long id, String name, boolean active) {
return "Java Provider Echo: id=" + id + ", name=" + name + ", active=" + active;
}
@Override
public Order mirrorOrder(Order order) {
return order;
}
@Override
public List<User> mirrorUsers(List<User> users) {
return users;
}
@Override
public Map<String, String> mirrorAttributes(Map<String, String> attrs) {
return attrs;
}
}
2.1.3 配置文件
来看下 dubbo 的配置文件,内容如下。
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 1. Dubbo 应用配置(和原配置一致) -->
<dubbo:application name="dubbo5-provider"/>
<!-- 2. Dubbo 协议配置(dubbo 协议 + 20880端口) -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 3. 注册中心配置(Zookeeper 地址 + 超时时间) -->
<dubbo:registry
address="zookeeper://127.0.0.1:2181"
timeout="30000"/>
<!-- 4. 暴露 Dubbo 服务(核心!和 Go 消费者完全对齐) -->
<!-- interface:Java接口全类名 | ref:服务实现类Bean | group/version:严格匹配Go端 -->
<dubbo:service
interface="com.demo.service.UserProfileService"
ref="userProfileServiceImpl"
group="dubbo-5"
version="1.0.0"
timeout="5000"/>
<!-- 5. 注册服务实现类(替换原注解扫描) -->
<bean id="userProfileServiceImpl" class="com.demo.service.impl.UserProfileServiceImpl"/>
</beans>
整体来说上面就是很常规的 dubbo 注册,下面来看下 go 的代码。
2.2 go 代码调用
2.2.1 接口提供
go
package service
import (
"context"
hessian "github.com/apache/dubbo-go-hessian2"
_ "github.com/apache/dubbo-go/cluster/cluster_impl"
_ "github.com/apache/dubbo-go/cluster/loadbalance"
_ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
"github.com/apache/dubbo-go/config"
_ "github.com/apache/dubbo-go/filter/filter_impl"
_ "github.com/apache/dubbo-go/registry/protocol"
_ "github.com/apache/dubbo-go/registry/zookeeper"
_ "github.com/apache/dubbo-go/protocol/dubbo"
)
var userProfile *UserProfileService
func init() {
config.Load()
svc := new(UserProfileService)
config.SetConsumerService(svc)
// dubbo 协议下只要跨进程传自定义结构体,就要先注册成 Hessian POJO。
hessian.RegisterPOJO(&Address{})
hessian.RegisterPOJO(&User{})
hessian.RegisterPOJO(&OrderItem{})
hessian.RegisterPOJO(&Order{})
userProfile = svc
}
// Address 对应 Java 侧的 com.demo.bean.Address
type Address struct {
City string `hessian:"city"`
Street string `hessian:"street"`
ZipCode string `hessian:"zipCode"`
}
func (*Address) JavaClassName() string {
return "com.demo.bean.Address"
}
// User 对应 Java 侧的 com.demo.bean.User
type User struct {
ID int64 `hessian:"id"`
Name string `hessian:"name"`
Age int32 `hessian:"age"`
Email string `hessian:"email"`
Address *Address `hessian:"address"`
}
func (*User) JavaClassName() string {
return "com.demo.bean.User"
}
// OrderItem 对应 Java 侧的 com.demo.bean.OrderItem
type OrderItem struct {
SKU string `hessian:"sku"`
Quantity int32 `hessian:"quantity"`
Price int64 `hessian:"price"`
}
func (*OrderItem) JavaClassName() string {
return "com.demo.bean.OrderItem"
}
// Order 对应 Java 侧的 com.example.dubbo5.api.Order。
// 它内部再嵌套 User、Address、List、Map,专门用来演示复杂 POJO。
type Order struct {
OrderNo string `hessian:"orderNo"`
Buyer *User `hessian:"buyer"`
Items []*OrderItem `hessian:"items"`
Tags map[string]string `hessian:"tags"`
}
func (*Order) JavaClassName() string {
return "com.demo.bean.Order"
}
// UserProfileService 是 consumer 侧的 dubbo stub。
// 这里不写业务实现,而是让 dubbo-go 在启动时把这些函数字段注入成远程调用代理。
type UserProfileService struct {
// 基本数据类型入参。
EchoBasic func(ctx context.Context, id int64, name string, active bool) (string, error) `dubbo:"echoBasic"`
// 嵌套 struct 入参。
MirrorOrder func(ctx context.Context, order *Order) (*Order, error) `dubbo:"mirrorOrder"`
// List 入参。
MirrorUsers func(ctx context.Context, users []*User) (interface{}, error) `dubbo:"mirrorUsers"`
// Map 入参。
MirrorAttributes func(ctx context.Context, attrs map[string]string) (map[interface{}]interface{}, error) `dubbo:"mirrorAttributes"`
}
func (u *UserProfileService) Reference() string {
return "UserProfileService"
}
func GetUserProfileService() *UserProfileService {
return userProfile
}
上面就是提供接口的所有代码,下面来看下里面的一些核心逻辑。
config.Load()加载所有配置文件,这个版本的 dubbo-go 配置文件要通过环境变量注入,就是下面的参数:CONF_CONSUMER_FILE_PATH=conf/dubbogo.yml;CONF_PROVIDER_FILE_PATH=conf/dubbogo.yml,指定消费者和生产者配置文件路径。- 参数类型,如果 Java 用的是基本数据类型,Go 里面也要用基本数据类型来对应,别用包装类,上面的
EchoBasic方法,Java 侧一开始我用的包装类,然后 Go 用基本数据类型传参和用指针传参都提示没办法找到 provider,没办法只能改成基本数据类型,这才调用成功。 - 返回值接收 ,注意上面的 MirrorUsers 和 MirrorAttributes,返回值接收全部用
interface{},集合类型的返回结果不能写成具体类型,否则会报错,只能用interface{}接收然后自己转,可能这个是框架的问题,不能自己转?反正我没找到办法处理这个问题,只能自己手动转。
2.2.2 配置文件
go
# dubbo client yaml configure file
check: false
# client
request_timeout: "3s"
# connect timeout
connect_timeout: "3s"
# application config
application:
name: "dubbo-5-consumer"
# registry config
registries:
"demoZk":
protocol: "zookeeper"
timeout: "3s"
address: "127.0.0.1:2181"
# reference config
references:
"UserProfileService":
registry: "demoZk"
protocol: "dubbo"
interface: "com.demo.service.UserProfileService"
cluster: "failover"
group: "dubbo-5"
version: "1.0.0"
methods:
- name: "echoBasic"
retries: 3
- name: "mirrorOrder"
retries: 3
- name: "mirrorUsers"
retries: 3
- name: "mirrorAttributes"
retries: 3
# protocol config
protocol_conf:
dubbo:
reconnect_interval: 0
connection_number: 1
heartbeat_period: "5s"
session_timeout: "180s"
pool_size: 64
pool_ttl: 600
getty_session_param:
compress_encoding: false
tcp_no_delay: true
tcp_keep_alive: true
keep_alive_period: "120s"
tcp_r_buf_size: 262144
tcp_w_buf_size: 65536
pkg_rq_size: 1024
pkg_wq_size: 512
tcp_read_timeout: "1s"
tcp_write_timeout: "5s"
wait_timeout: "1s"
max_msg_len: 1024000
session_name: "client"
1.x 版本的写法和 3.x 版本的写法不一样,有很大不同,上面是 1.x 的配置文件写法。
2.2.3 consumer main 方法
最后来看下 consumer 的 main 方法。
go
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"example.com/dubbo-5/service"
_ "github.com/apache/dubbo-go/cluster/cluster_impl"
_ "github.com/apache/dubbo-go/cluster/loadbalance"
_ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
"github.com/apache/dubbo-go/config"
_ "github.com/apache/dubbo-go/filter/filter_impl"
_ "github.com/apache/dubbo-go/registry/protocol"
_ "github.com/apache/dubbo-go/registry/zookeeper"
_ "github.com/apache/dubbo-go/protocol/dubbo"
)
func main() {
svc := service.GetUserProfileService()
config.Load()
ctx := context.Background()
basicResp, err := svc.EchoBasic(ctx, 1001, "jiang", true)
if err != nil {
log.Fatalf("echoBasic error: %v", err)
}
log.Printf("echoBasic response: %s", basicResp)
orderResp, err := svc.MirrorOrder(ctx, sampleOrder())
if err != nil {
log.Fatalf("mirrorOrder error: %v", err)
}
log.Printf("mirrorOrder response:\n%s", pretty(orderResp))
usersResp, err := svc.MirrorUsers(ctx, sampleUsers())
if err != nil {
log.Fatalf("mirrorUsers error: %v", err)
}
res := usersResp.([]interface{})
for _, item := range res {
user := item.(*service.User)
fmt.Printf("%v\n", user)
}
attrsResp, err := svc.MirrorAttributes(ctx, map[string]string{
"source": "go-consumer",
"scene": "dubbo-5",
})
log.Printf("mirrorAttributes response:\n%v", attrsResp)
}
func sampleOrder() *service.Order {
return &service.Order{
OrderNo: "DUBBO5-10001",
Buyer: &service.User{
ID: 1,
Name: "jiang",
Age: 20,
Email: "jiang@example.com",
Address: &service.Address{
City: "Shanghai",
Street: "Pudong Avenue",
ZipCode: "200120",
},
},
Items: []*service.OrderItem{
{SKU: "book-01", Quantity: 2, Price: 99},
{SKU: "pen-01", Quantity: 3, Price: 15},
},
Tags: map[string]string{
"channel": "web",
"level": "vip",
},
}
}
func sampleUsers() []*service.User {
return []*service.User{
{
ID: 1,
Name: "alice",
Age: 18,
Email: "alice@example.com",
Address: &service.Address{
City: "Beijing",
Street: "Chaoyang Road",
ZipCode: "100020",
},
},
{
ID: 2,
Name: "bob",
Age: 22,
Email: "bob@example.com",
Address: &service.Address{
City: "Shenzhen",
Street: "Nanshan Road",
ZipCode: "518000",
},
},
}
}
func pretty(v any) string {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
return "<marshal error>"
}
return string(data)
}
2.2.4 启动输出
go
2026/05/05 14:42:56 echoBasic response: Java Provider Echo: id=1001, name=jiang, active=true
2026/05/05 14:42:56 mirrorOrder response:
{
"OrderNo": "DUBBO5-10001",
"Buyer": {
"ID": 1,
"Name": "jiang",
"Age": 20,
"Email": "jiang@example.com",
"Address": {
"City": "Shanghai",
"Street": "Pudong Avenue",
"ZipCode": "200120"
}
},
"Items": [
{
"SKU": "book-01",
"Quantity": 2,
"Price": 99
},
{
"SKU": "pen-01",
"Quantity": 3,
"Price": 15
}
],
"Tags": {
"channel": "web",
"level": "vip"
}
}
map[scene:dubbo-5 source:go-consumer]
&{1 alice 18 alice@example.com 0xc0002112f0}
&{2 bob 22 bob@example.com 0xc000211320}
可以看到确实调用 Java 接口成功了。
3. 小结
好了,dubbo 相关的内容就先学到这,主要是日常开发的时候用到的主要也是 go 调用 java 代码。
如有错误,欢迎指出!!!