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("太多的字節")