1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        字節(jié)跳動(dòng)踩坑記:一知半解protobuf

        共 975字,需瀏覽 2分鐘

         ·

        2020-09-17 02:18

        本篇寫個(gè)小坑,別期望太高…




        在廣告系統(tǒng)里,對(duì)延遲是毫秒必爭(zhēng)(畢竟省下來的每一毫秒都可以用在后端優(yōu)化效果),因此我們和外部媒體之間的通信往往使用 protobuf 。


        相比 json、xml,protobuf 確實(shí)節(jié)省了不少編解碼的時(shí)間以及網(wǎng)絡(luò)開銷,不過相應(yīng)的代價(jià)是犧牲了便利性不能用 vi 等文本編輯器查看/修改,遇到問題時(shí)排查也比較麻煩。




        - 入坑 -


        比如 7 月份,某媒體希望一次請(qǐng)求中拉到多條廣告(用于信息流場(chǎng)景),因此在 imp 添加一個(gè) ads_count 字段,用于標(biāo)識(shí)本次請(qǐng)求需要的廣告數(shù)量。


        過程是這樣,在?xxx.proto 里給?Impression 類型添加一個(gè)新字段

        package com.xxx;message BidRequest {??string?id?=?1;??int32?ver?=?2;  message Impression {????...????int32?ads_count?=?9;  }??Impression?imp?=?3;  ...}


        然后用 protoc 編譯,生成新版的?xxx.pb.go?

        $ protoc --go_out=. xxx.proto


        看起來挺簡(jiǎn)單一個(gè)流程,結(jié)果還是出了問題:不論媒體請(qǐng)求中填了什么值,這邊 decode 出來,imp.GetAdsCount() 得到的總是 1 。




        - 排查?-


        由于我方代碼是自測(cè)過的,能夠正常取到 ads_count 的值,因此猜測(cè)是對(duì)方請(qǐng)求有點(diǎn)啥問題。


        于是將對(duì)方的請(qǐng)求錄下來,存到文件 req.pb 中,然后用?protoc 暴力解碼:

        $?protoc?--decode-raw?req.pb1 {  6: 0x3938373635343332}2: 13 {  1: 1  2: "6f63bd4df111480"  3: 1}...


        可以看到,我們什么也沒看懂。



        不過還好我們有 xxx.proto,借助已知信息,可以更好地解碼請(qǐng)求:

        $?protoc?--decode=com.xxx.BidRequest?xxx.proto??id: "123456789"ver: 1imp {??id:?1??...  ads_count: 1  10: 3}...


        看到了點(diǎn)不太對(duì)的東西。




        - 填坑?-


        在 imp 里面,除了 ads_count 之外,還看到了個(gè) "10: 3"。


        由于 protobuf 的變量名不能是純數(shù)字,所以這應(yīng)當(dāng)是某個(gè)在類型定義里沒有出現(xiàn)的字段,decode時(shí)只能用其序號(hào)代替,由此可知,應(yīng)該是的 proto?文件應(yīng)該有些差異。


        經(jīng)過溝通,媒體確實(shí)在 ads_count 之前還加了另一個(gè)字段(可能是和其他合作方使用到的);雙方對(duì)齊以后,問題順利解決:


        修正 ads_count 的序號(hào):

          message Impression {????...????int32?ads_count?=?10;  }


        用正確的 proto 來 decode:

        $?protoc?--decode=com.xxx.BidRequest?xxx.proto??id: "123456789"ver: 1imp {??id:?1  ...??ads_count:?3}...


        MISSION COMPLETED.




        - encoding?-


        問題是解決了,但是只寫這些就顯得太應(yīng)付了,就再介紹下 proto 文件是怎么編解碼的吧。


        官方有一篇很詳細(xì)的文檔介紹了編碼的過程(詳見文末“閱讀原文”),這里摘一些重點(diǎn)。


        以一個(gè)簡(jiǎn)單的類型為例:

        message Test1 {??optional?int32 a = 1;}

        如果給 a 賦值 150 并序列化,會(huì)得到3個(gè)字節(jié)(16進(jìn)制):

        08?96?01

        其中第一個(gè)字節(jié)(08)是一個(gè) varint(每個(gè)字節(jié)的最高位 = 1 表示該 int 還需要拼上后續(xù)字節(jié)的低 7 bits),其內(nèi)容包含了第一個(gè)元素的序號(hào)(field number)和類型(wire type)。


        將 08 的二進(jìn)制 "0000 1000" 拆分成三部分來解釋:

        • 0

          • 表示這個(gè) varint 到這個(gè)字節(jié)就結(jié)束了

        • 0001

          • 表示其序號(hào)是1

        • 000

          • 表示其值類型也是個(gè) varint


        注意,不管這個(gè) varint 有多大,其末3位總是用于表示類型(wire type),可能的取值有:

        • 0:?varint

        • 1:?64-bit,如 fixed64, sfixed64, double

        • 2:?指定長(zhǎng)度類型,如 string, bytes, 內(nèi)嵌類型

        • 5:?32-bit,如 fixed32, sfixed32, float


        第2、3個(gè)字節(jié)(96?01)是 a 的值,其二進(jìn)制表示是

        1001?0110 0000 0001

        第 2 字節(jié)的最高位是 1 ,我們知道這個(gè) varint 還沒結(jié)束;而第 3 字節(jié)的最高位是 0 ,這個(gè) varint 就到此結(jié)束了。


        將兩個(gè)最高位去掉,拼出一個(gè)完整的二進(jìn)制數(shù):

        0000001?0010110 = 150

        注意:varint 按字節(jié)序是小端存儲(chǔ),因此第 3 個(gè)字節(jié)的 0000001 放在高位。




        - signed integers?-


        varint 看起來是個(gè)好東西,因?yàn)閷?shí)踐中經(jīng)常會(huì)用到一些枚舉值,可能的取值范圍很小,使用 varint 只需要少量的空間。


        不過如果我們需要用 -1 的時(shí)候怎么辦呢?不管是用反碼還是補(bǔ)碼,都需要考慮符號(hào)位的問題? —— 對(duì)于 int32/int64,負(fù)數(shù)的編碼總是要占用 10 個(gè)字節(jié)。


        protobuf 的解決方案是為 sint32/sint64 引入 "ZigZag encoding",簡(jiǎn)單來說就是交替使用 0,1,2,3,... 來表示 0,-1,1,-2,...,從而將較小的負(fù)數(shù)編碼為較小的無符號(hào)數(shù),再使用 varint 編碼。




        - 沒了?-


        就這樣吧,更多細(xì)節(jié)(string、內(nèi)嵌類型以及數(shù)組的編碼),請(qǐng)參考官方文檔(文末“閱讀原文”)。


        最后一個(gè)小問題,下面這個(gè)編碼后的消息,表示什么意思呢?

        12?03?36 36 36





        推薦閱讀



        學(xué)習(xí)交流 Go 語言,掃碼回復(fù)「進(jìn)群」即可


        站長(zhǎng) polarisxu

        自己的原創(chuàng)文章

        不限于 Go 技術(shù)

        職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)


        Go語言中文網(wǎng)

        每天為你

        分享 Go 知識(shí)

        Go愛好者值得關(guān)注



        瀏覽 79
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产寡妇婬乱A毛片91精品 | 操比大片| 日韩在线免费 | 黄色录像免费看 | 囗交26式的姿势有哪些 | 啊啊啊啊轻点视频 | 自拍偷拍第8页 | 五月丁香婷中文 | 国产一级婬乱95视频 | 国产日韩一区二区三免费 |