主要用法
- 和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
|
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里配置的一样
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的生态