最近在设计一个P2P分布式存储系统,网络路由方面采用的是Kalelima协议,在这个协议的实现中主要有四个RPCs:
- Ping:节点发起ping请求,主要用来检测与其他节点网络连接是否通顺
- Store:存储请求,参数为<key, value>键值对,key是value的SHA1值,value是要保存的数据。
- FindNode:寻找节点,参数是一个节点SHA1值,返回的是一个三元组<ip, 端口, 节点id>集合
- FindValue:寻找值,参数是一个文件的SHA1值,如果不存在该文件,则返回距离这个文件最近的一个三元组<ip, 端口, 节点id>集合,如果存在文件,则直接返回文件的数据内容
我对.proto的设计如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.dust.grpc.kalelima";
option java_outer_classname = "KalelimaServiceProto";
// 定义的一个服务
service KalelimaService {
// Ping函数
rpc Ping(PingPackage) returns (PingPackage) {}
// 存储
rpc Store(StoreRequest) returns (StoreResponse) {}
// 寻找节点
rpc FindNode(FindRequest) returns (stream FindNodeResponse) {}
// 寻找值
rpc findValue(FindRequest) returns (stream FindValueResponse) {}
}
//ping函数发起的请求,携带发起者的id以及发起时间,收到ping之后的节点将id修改成自身id,时间不变原样返回
message PingPackage {
string nodeId = 1;
int32 timestamp = 2;
}
//存储请求,携带有一个sha1编码的key和数据对象。其中key还是该对象的checksum
message StoreRequest {
string key = 1;
bytes data = 2;
}
//存储响应,code = 1表示成功;code = 0表示失败。当code = 0的时候errmsg才会有值
message StoreResponse {
int32 code = 1;
string errmsg = 2;
}
//寻找请求,key既可以表示节点id,又可以表示文件id。具体根据请求的接口来判断
message FindRequest {
string key = 1;
}
//寻找节点返回,返回的是一个三元组<IP地址、端口、节点id>列表
message FindNodeResponse {
string host = 1;
int32 port = 2;
string nodeId = 3;
}
//寻找文件呢返回,如果文件不存在则返回离这个文件最近的三元组<IP地址、端口、节点id>列表,否则直接返回值
message FindValueResponse {
string host = 1;
int32 port = 2;
string nodeId = 3;
bytes data = 4;
}
然后在生成代码的时候问题来了,这里官方会给出两种生成方式:1、使用protoc编译器生成;2、使用maven插件生成
一开始我觉得改xml麻烦就直接通过protoc生成,生成的结果让我有点疑惑:
我的Service呢?我这么大的一个Service呢?其中的KalelimaServiceProto
并不是一个服务文件的接口,而是类似于描述文件的东西。
百度之后发现是缺少了一行命令option java_generic_services = true;
加上后终于有Service了。
看着也没问题,是一个抽象类,其中定义了.proto文件中定义的抽象方法。好的那就去实现它吧!
实现之后问题来了,我该如何将这个服务注册在服务端中呢?查询了一些文档发现许多教程都是通过maven生成的代码,那我用maven生成代码中创建服务端的部分将这个Service注册进去可以吗?
不可以!虽然grpc有addService方法,但是他们两个传入的Service和protoc生成的Service完全不是一个接口:1
2
3
4
5
6
7
8
9public interface Service {
ServiceDescriptor getDescriptorForType();
void callMethod(MethodDescriptor var1, RpcController var2, Message var3, RpcCallback<Message> var4);
Message getRequestPrototype(MethodDescriptor var1);
Message getResponsePrototype(MethodDescriptor var1);
}
1 | public interface BindableService { |
甚至连jar包都不是同一个!
查询了protobuf的官方文档后发现,proto和grpc应该是这样的一种关系:
protobuf只是grpc的一小部分,负责传输协议,而不是RPCs!
因此如果只要grpc的话需要自己手动实现自己的RPC。
为了这个我浪费了快3小时。
最后还是老老实实用grpc,至于自己实现RPCs,就要等到后面一个版本了,将Vertx与protobuf结合感觉不要太香。