主要用法

  • 和grpc中使用protobuf生成代码基本一致(至少在形式上),直接看例子吧
1
2
3
4
5
6
7
8
9
├── go-client
│   ├── client.go
│   ├── client.yml
├── go-server
│   ├── main.go
│   └── server.yml
└── user
├── user.pb.go
└── user.proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
syntax = "proto3";

package user;

service UserProvider {
rpc GetUser (UserRequest) returns (UserReply) {}
}

message UserRequest {
string id = 1;
}

message UserReply {
string id = 1;
string name = 2;
int32 age = 3;
}
  • 使用protoc-gen-dubbogo插件生成dubbogo的代理
1
protoc --plugin={plugin_path} --dubbogo_out=plugins=dubbogo:. user/user.proto
  • client关键代码
1
2
3
4
5
6
7
user := user.NewUserProvider()
reply := user.UserReply{}
err := userProvider.GetUser(context.TODO(), &user.UserRequest{Id: "A001"}, &reply)
if err != nil {
log.Fatal(err)
}
println("response result: %+v", reply)

ps: 一切都是熟悉的味道
ps: 生成代理的名称需要和reference里配置的一样

  • server关键代码
1
2
3
4
5
6
7
8
9
10
type UserProvider struct {
pb.UnimplementedUserProviderServer
}

func (*UserProvider) GetUser(ctx context.Context, user *pb.UserRequest) (*pb.UserReply, error) {
return &pb.UserReply{Id: "001", Name: "alice", Age: 18}, nil
}

// 注册
pb.RegisterProvider(new(UserProvider))

实现原理

  • 在dubbogo抽出一层serialization,任何和serialization相关的之后只要实现Serialize接口就行了,这样是为了更好的实现更多序列化的支持,逻辑上会更合理一些,原有的go hessian2中做了一部分dubbo相关的codec工作,这里我也把它抽到dubbogo中了, 当然hessian2的序列化仍然保留了,这次实现是兼容老版本的。
1
2
3
4
5
type Serializer interface {
Marshal(p DubboPackage) ([]byte, error)
Unmarshal([]byte, *DubboPackage) error
}

  • 参考了dubbo的protobuf实现,实现了在protobuf层面和java互通(不一定是好事:()
  • 其他的就是细节了

一些注意点

  • error的处理和java不太一样,java会把详细的java error stack都返回给客户端,go只会把message传过来,生成一个error
  • 由于java protobuf生成的代理方法名是小写开头(完全搞不明白是为什么),这在golang中表示私有方法,个人已经提了issue, 所以直接用java的例子是不行的
  • java protobuf的代理生成的是内部接口,比如xxx$IDemoService, $是url中的一个特殊字符,正好发现了dubbogo的一个注册url的bug

我为什么要支持protobuf

  • protobuf的语言中立性更好,序列化性能也更好
  • 更加符合golang的生态