天天看點

大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外

    個人感覺序列化簡單來說就是按一定規則組包。反序列化就是按組包時的規則來接包。正常來說。序列化不會很難。不會很複雜。因為過于複雜的序列化協定會導緻較長的解析時間,這可能會使得序列化和反序列化階段成為整個系統的瓶頸。就像壓縮檔案、解壓檔案,會占用大量cpu時間。

    是以正常的序列化會在時間和空間上考慮。個人感覺對于電商業務時間應該是相對重要些。畢竟使用者沒有那麼多時間等你解析。

           我們是用thrift來序列化的。一份thrift檔案生成2份。一份是c++生成的用來編寫服務接口。一份是php生成的。所有請求都會先落到前端機器。然後前端機器用php調用服務端函數接口。傳回處理結果。這其實是遠端調用rpc。

說序列化之前先說下平台給序列化配置設定的buf的空間大小

1、每個協程會配置設定大概固定標頭(56個位元組)+特殊buf(200個位元組)的空間來儲存標頭。是以首先如果收到的包特殊buf(就是放sessionkey和uid等資訊)大于200個位元組。會報錯不處理。但是并不會給netio 傳回一個錯誤包消息。是以用戶端 會一直等到用戶端設定的逾時時間

2、每個container會配置設定3M的空間來處理資料。是以去掉標頭和特殊buf.剩下的就是可以用來序列化的空間3*1024*1024-固定標頭-特殊buf。   是以最少會有 3*1024*1024-56-200的空間

        這裡其實可以看到協程的好處。這個3M的空間。對于每個協程來說是共享的。因為我們是協程的方式,其實是一種順序流程,沒有協程會跟你競争使用這個buf的資源。因為可以自己手動控制協程的切換。

        如果是多線程的話。可能就要對這個buf加鎖。競争這一個全局資源來處理資料。這也是多線程程式設計被诟病的一個地方,需要加鎖。

大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外
大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外
大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外

用戶端直接調用函數接口。到服務端請求結果

最後需要序列化的東西如下是類_Cao_action_AddActionSupplier_Req

函數的入參都是我們需要序列化的内容。注意這裡是rpc調用的一個關鍵點。

a)    先看下我們的thritf

如果下圖。發現我們的函數入參也是打上了tag标志的。作用跟我們在結構體中打tag标志是一樣的。為了辨別一個字段的含義。

序列化的時候把這些tag序列化進去。 然後反序列化的時候靠這些tag來解析

大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外

b ) 先把圖貼出來。按着圖來講更清晰些

大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外

c)   首先我們會建立一個CByteStream的類來。序列化内容。在CByteStream的構造函數會自動寫入一個位元組的序列化標頭。值為1

<code>CByteStream(</code><code>char</code><code>* pStreamBuf = NULL, uint32_t nBufLen = 0,</code><code>bool</code> <code>bStore=</code><code>true</code><code>, </code><code>bool</code> <code>bRealWrite = </code><code>true</code><code>);</code>

pStreamBuf  是序列化buf指針

pStreamBuf  是序列化的長度

bStore  true表示是否需要包資料存儲下來。  false表示不需要把資料存下來

bRealWrite 表示是否支援讀寫buf

d)   接着就開始寫類_Cao_action_AddActionSupplier_Req的成員變量。其實就是函數入參。寫的時候是先協tag就是下圖中的fid。  其實就是在thrift中已經寫好的函數入參的tag值。

具體寫的過程我們先看簡單基本類型。比如strMachineKey

     1)先寫tag。  strMachineKey 的tag為1.  程式裡規定tag占兩個位元組。是以函數入參可以是0xffff個。

     2 ) 接着會寫4個固定的位元組。用來存儲後面緊跟着資料的值。這裡strMachineKey的長度是512000.

     3 ) 寫内容 。  把strMachineKey的内容寫入緊跟着的buf

針對整形和長整形就不說了

大同小異

大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外

e) 接着我們關注下 是怎麼寫結構體oActionInfo的。 

     1)先寫tag。  oActionInfo 的tag為5.  程式裡規定tag占兩個位元組。

     2 ) 接着針對結構體這裡 會寫4個固定的位元組用來存結構體序列化長度。因為開始不知道是以值為0。

     3 ) 接着寫字段DistributorId。  它在oActionInfo結構體中的tag值為6.類型為int64. 是以先寫tag=6占兩個位元組,接着配置設定4個位元組存長度。最後配置設定8個位元組存内容

     4)跟着寫DisShopId字段。就不細說了

     5)最後寫了2個位元組包尾

     6)最後 回寫結構體的長度 

這裡注意下寫結構體時候的寫法。不注意的話會看錯。

1)這裡先拿到開始寫結構體的buf指針。注意這裡是用的int32_t。占四個位元組。跟前面保持一直。這裡用來的存後面總序列虛化結構體提的總長度。

2)由于剛開始的時候  并不知道後面的結構體會序列化多少個位元組。是以這裡先寫4個位元組。

同時把這便宜的4個位元組的記憶體值 設定為0 

bs&lt;&lt;0;  (這裡其實建議寫成  bs&lt;&lt;int32_t(0) 會好一點。開起來一緻)

這裡開始沒注意。以為寫4個位元組值為0的 結構體的頭。其實這裡是放結構體長度的

3)最後第5步。 重新指派 結構體的長度

f)最後對整個_Cao_action_AddActionSupplier_Req寫了兩個位元組的包尾

g) 我麼可以看到oActionInfo其實有一堆的字段。但是我們在請求的時候隻寫了兩個字段。所有在序列化的時候也隻序列化了兩個字段

大型分布式C++架構《三:序列化與反序列化》一、前言 二、配置設定序列化空間的大小三、序列化步驟四、序列化解析五、話外

其實我們可以看到我們的這種序列化,很整齊。很規則。比較緊湊。但是并不節省空間。這個裡面有很多資料可以壓縮的。但是壓縮帶來一個問題就是解壓的時候很消耗cpu的。跟我的業務場景不服和。也沒必要。

其實知道了資料是怎麼寫入的  解析起來就很容易了。其實這種序列化就是兩邊約定規則。知道規則以後就可以解析了

        解析的具體步驟就不詳細說了。這裡說下解析的時候幾個特殊的地方

1、因為tag占2個位元組。是以函數入參不能大于0xffff. 一個結構體的字段個數不能大于0xffff

2、假如前端傳入的tag在解析端找不到。解析端會偏移處理下一個tag。是以這是為什麼我們可以删除字段的原因。

比如前端傳入的結構如下

struct A{

 1:int  aa

 2:int  bb

}

但是服務端背景編譯後删除了一個字段

    1:int  aa

a)如果前端隻填了字段aa。  那麼解析起來沒有任何問題.因為不會把字段bb的任何資訊序列化進去。

b)假如前端填了 aa 和 bb字段。

那麼服務端在解析的時候。拿到tag2。發現找不到對應的資料。

那麼它會偏移4個位元組取tag2對應字段内容所占的位元組數。比如這裡是4.

接着它發現是4.就偏移4個位元組。不處理字段值内容。直接取下一個tag進行處理

這也就是我們為什麼能删除字段的原因。

這樣看來我們的函數入參其實也是可以删除的

3、我們服務端新增字段重新編譯。前端沒有對應的tag。根本不會序列化進來。這也是我們可以增加字段的原因。

4、解析的時候如果發現tag為0.則會是認為解析結束。是以我們的tag是不能為0的

5、這樣我們也就能為服務端函數增加入參的。 同一個函數比如前端的入參是4個。服務端可以增加N個. 但是注意不能占用   函數已經用的tag。否則會有問題。而且為了保證函數的統一性。最好别這麼做。

6、到這裡已經很清晰了。 最後再說一次不能改tag對應的類型。

我們的這一套就是遠端調用rpc服務。通過我們的序列化。

其實就能了解所謂的RPC服務是什麼樣的。

說白了,遠端調用就是将對象名、函數名、參數等傳遞給遠端伺服器,伺服器将處理結果傳回給用戶端。

為了能解析出這些資訊。在入參的時候做上辨別(這裡是打tag).

谷歌的protobuf也用過。跟thrift其實差不多但是序列化和反序列的話的具體實作是有些不同的。

谷歌的protobuf更節省空間

以前具體看過序列化的源碼。覺得序列化反序列化以及rpc很神秘。現在看了源碼才發現确實寫的确實好,

但是沒那麼神秘裡。其實就是按一定規則組包。是以還是要多看源碼啊。

我們用的thrift就是 facebook的thrift。但是改了些東西。大體是一樣的。