Server 结构
结构参照hadoop RPC结构,自己造轮子
传输的数据结构
1,abstract class Server 接收并且响应客户端请求,把请求数据封装成Call 交给之类实现
2,客户端首次连接必须发送头”HEADER”+version+ServiceClass(Server 实现的之类)+auth
*|——–4byte——-|———-3byte—————–|
*|———————-|—-8 bit—-8 bit—–8bit——|
*|——–HEADER—–|–version ServiceClass auth—|
3,call结构
*|——–4byte———-|———————-dataLength———————————|
*|————————| –len(1~4byte)-|—data—— |–len(1~4byte)-|—data——–|
*|————————| –RpcRequestHeaderProto– |–?RequestProto—————|
*|—-请求数据的长度—-|—————具体的请求数据————————————|
Server处理client 数据(java)
- 首先读取4个字节的看看是不是RPC请求
- 然后读取3个字节比对版本,Server具体的实现类,认证信息
- 循环开始读取call
1.读取4个字节(一个Int)表示当前call的数据长度
2,读取call的数据长度的数据
3,封装成Call对象,放入Call队列
处理Call队列
- 循环读取call
- 根据varint解压缩读取RpcRequestHeader的长度
- 通过长度读取数据
- 通过RpcRequestHeaderProto.parseFrom 解析出RequestHeader
- 通过RequestHeader的protoName+version 找到具体处理类(BlockingService service)
- MethodDescriptor method = service.getDescriptorForType().findMethodByName(methodName); 找到执行的方法
- Message requestPrototype = service.getRequestPrototype(method);找到请求参数类型
- 根据varint解压缩读取request的长度
- 通过长度读取数据
- requestPrototype.newBuilderForType().mergeFrom(数据).build()
- Message result= service.callBlockingMethod(method, null,requestPrototype.newBuilderForType().mergeFrom(request.theRequestRead).build()); 获取返回对象
- 生成返回头(RpcResponseHeaderProto)
- 把RpcResponseHeaderProto+result(同样写入总长度+header的长度+header+response的长度+response)放入响应队列
响应队列
- 读取数据返回给客户端
客户端发送数据
- c = ClientProtocolService_Stub(service.RpcChannel) service.RpcChannel表示继承service.RpcChannel的实例,重写CallMethod
- c.echo 实际就是调用service.RpcChanneld 的CallMethod方法,参数为 EchoRequestProto
- 根据方法产生RpcRequestHeaderProto实例
- 把(总数据长度+RpcRequestHeaderProto长度的varint压缩+RpcRequestHeaderProto.SerializeToString+EchoRequestProto长度的varint压缩+EchoRequestProto.SerializeToString)发送到服务器
- 等待数据返回,
1.读取总返回的数据长度,固定4个字节
2.根据长度读取数据
- 根据varint解压缩读取RpcResponseHeaderProto的长度
- 读取RpcResponseHeaderProto数据,responseHeader.ParseFromString解析数据
- 判断是否成功,失败显示错误信息退出
- 如果成功,根据varint解压缩读取EchoResponseProto的长度和数据
代码
服务器是java(因为python没找到select.select唤醒的方法所以使用java写),客户端是python
https://github.com/neo-hu/RPC
注意
数据的长度
java的writeDelimitedTo写入的数据会先写入这个Msg的数据的长度,是1~4字节的数据
如果java解析 parseDelimitedFrom,
python没这方法(也许是我没找到)需要直接先写入长度,在写入数据
varint
def write_raw_varint(value):
"""
varint压缩
"""
local_chr = _PY2 and chr or (lambda x: bytes((x,)))
pieces = []
write = lambda x: pieces.append(local_chr(x))
bits = value & 0x7f
value >>= 7
while value:
write(0x80 | bits)
bits = value & 0x7f
value >>= 7
write(bits)
return ''.join(pieces)
def read_raw_varint(buff):
"""
varint解压缩,每次读取8为,如果第一位是1表示还有数据,把后7位保存为tmp,直到读取到第一位为0的,然后把所有的tmp后7连接起来,倒序连接
"""
s1 = struct.Struct("!b")
shift = 0
result = 0
index = 0
while True:
tmp = buff[index]
index += 1
b, = s1.unpack(tmp)
result |= (b & 0x7f) << shift
if not (b & 0x80):
return index, result
shift += 7
if shift >= 64:
raise Exception("太多的字节")