protobuf RPC實現

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)

  1. 首先讀取4個字節的看看是不是RPC請求
  2. 然後讀取3個字節比對版本,Server具體的實現類,認證信息
  3. 循環開始讀取call
    1.讀取4個字節(一個Int)表示當前call的數據長度
    2,讀取call的數據長度的數據
    3,封裝成Call對象,放入Call隊列

處理Call隊列

  1. 循環讀取call
    1. 根據varint解壓縮讀取RpcRequestHeader的長度
    2. 通過長度讀取數據
    3. 通過RpcRequestHeaderProto.parseFrom 解析出RequestHeader
    4. 通過RequestHeader的protoName+version 找到具體處理類(BlockingService service)
    5. MethodDescriptor method = service.getDescriptorForType().findMethodByName(methodName); 找到執行的方法
    6. Message requestPrototype = service.getRequestPrototype(method);找到請求參數類型
    7. 根據varint解壓縮讀取request的長度
    8. 通過長度讀取數據
    9. requestPrototype.newBuilderForType().mergeFrom(數據).build()
    10. Message result= service.callBlockingMethod(method, null,requestPrototype.newBuilderForType().mergeFrom(request.theRequestRead).build()); 獲取返回對象
    11. 生成返回頭(RpcResponseHeaderProto)
    12. 把RpcResponseHeaderProto+result(同樣寫入總長度+header的長度+header+response的長度+response)放入響應隊列

響應隊列

  1. 讀取數據返回給客戶端

客戶端發送數據

  1. c = ClientProtocolService_Stub(service.RpcChannel) service.RpcChannel表示繼承service.RpcChannel的實例,重寫CallMethod
  2. c.echo 實際就是調用service.RpcChanneld 的CallMethod方法,參數爲 EchoRequestProto
  3. 根據方法產生RpcRequestHeaderProto實例
  4. 把(總數據長度+RpcRequestHeaderProto長度的varint壓縮+RpcRequestHeaderProto.SerializeToString+EchoRequestProto長度的varint壓縮+EchoRequestProto.SerializeToString)發送到服務器
  5. 等待數據返回,
    1.讀取總返回的數據長度,固定4個字節
    2.根據長度讀取數據
    1. 根據varint解壓縮讀取RpcResponseHeaderProto的長度
    2. 讀取RpcResponseHeaderProto數據,responseHeader.ParseFromString解析數據
    3. 判斷是否成功,失敗顯示錯誤信息退出
    4. 如果成功,根據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("太多的字節")

發佈了54 篇原創文章 · 獲贊 13 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章