1. 程式人生 > >大型分散式C++框架《三:序列化與反序列化》

大型分散式C++框架《三:序列化與反序列化》

原貼:http://www.cnblogs.com/ztteng/p/5381096.html

一、前言 

    個人感覺序列化簡單來說就是按一定規則組包。反序列化就是按組包時的規則來接包。正常來說。序列化不會很難。不會很複雜。因為過於複雜的序列化協議會導致較長的解析時間,這可能會使得序列化和反序列化階段成為整個系統的瓶頸。就像壓縮檔案、解壓檔案,會佔用大量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加鎖。競爭這一個全域性資源來處理資料。這也是多執行緒程式設計被詬病的一個地方,需要加鎖。

三、序列化步驟

1、我們先看下請求。

複製程式碼
    oCntlInfo.setOperatorUin(10458);
    oCntlInfo.setOperatorKey("abcde");
    oCntlInfo.setRouteKey(1234);
 
    std::string source = "aaaaa";
    std::string inReserve;
    std::string errmsg;
    std::
string outStr; std::string machineKey; for(int i =0;i<500*1024;i++) { machineKey.append("a"); } AoActionInfo oActionInfo; oActionInfo.SetDisShopId(1111); oActionInfo.SetDistributorId(2222); uint32_t dwResult = Stub4App.AddActionSupplier( oCntlInfo, machineKey, source, 1, 1, oActionInfo, inReserve, errmsg, outStr); if(dwResult == 0) { std::cout << "Invoke OK!" << std::endl; std::cout << "Invoke OK!" << std::endl; }
複製程式碼

客戶端直接呼叫函式介面。到服務端請求結果

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

函式的入參都是我們需要序列化的內容。注意這裡是rpc呼叫的一個關鍵點。

2、序列化開始

a)    先看下我們的thritf

如果下圖。發現我們的函式入參也是打上了tag標誌的。作用跟我們在結構體中打tag標誌是一樣的。為了標識一個欄位的含義。

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

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

c)   首先我們會建立一個CByteStream的類來。序列化內容。在CByteStream的建構函式會自動寫入一個位元組的序列化包頭。值為1 CByteStream(char* pStreamBuf = NULL, uint32_t nBufLen = 0,bool bStore=truebool bRealWrite = true);
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 針對整形和長整形就不說了 大同小異 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<<0;  (這裡其實建議寫成  bs<<int32_t(0) 會好一點。開起來一致) 這裡開始沒注意。以為寫4個位元組值為0的 結構體的頭。其實這裡是放結構體長度的 3)最後第5步。 重新賦值 結構體的長度
1)int32_t* pLen = (int32_t*)bs.getRawBufCur();
2)bs << 0;
3)int32_t iLen=bs.getWrittenLength();
4)Serialize_w(bs);
5)*pLen = bs.getWrittenLength() - iLen;

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

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

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

四、序列化解析

其實知道了資料是怎麼寫入的  解析起來就很容易了。其實這種序列化就是兩邊約定規則。知道規則以後就可以解析了
        解析的具體步驟就不詳細說了。這裡說下解析的時候幾個特殊的地方

1、因為tag佔2個位元組。所以函式入參不能大於0xffff. 一個結構體的欄位個數不能大於0xffff 2、假如前端傳入的tag在解析端找不到。解析端會偏移處理下一個tag。所以這是為什麼我們可以刪除欄位的原因。 比如前端傳入的結構如下 struct A{  1:int  aa  2:int  bb } 但是服務端後臺編譯後刪除了一個欄位 struct A{     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。但是改了些東西。大體是一樣的。


相關推薦

大型分散式C++框架序列序列

原貼:http://www.cnblogs.com/ztteng/p/5381096.html 一、前言      個人感覺序列化簡單來說就是按一定規則組包。反序列化就是按組包時的規則來接包。正常來說。序列化不會很難。不會很複雜。因為過於複雜的序列化協議會導致較

大型分散式C++框架《一框架簡介》

  下面簡單介紹下情況。框架是騰訊電商平臺的分散式框架。雖然騰訊拍拍已經玩完了。但是這套框架還是很不錯的。而且據原騰訊同事說微信也是用的這套框架。原始碼肯定是不能說的。但是介紹大體的思想我想應該沒問題。雖然在這個框架下寫了一年多的業務程式碼。但是平臺框架的程式碼一直沒怎麼看。最近有開始看平臺程式碼雖然沒看完。

Java序列序列連問是什麼?為什麼要?如何做?

Java序列化與反序列化是什麼? Java序列化是指把Java物件轉換為位元組序列的過程,而Java反序列化是指把位元組序列恢復為Java物件的過程: 序列化:物件序列化的最主要的用處就是在傳遞和儲存物件的時候,保證物件的完整性和可傳遞性。序列化是把物件轉換成有序位元組流,以便在網路上傳輸或者儲存在本地檔

C#對象序列序列

space ros 個人信息 瀏覽器 特性 點名 文件名 屬性節點 派生 1.對象序列化的介紹 (1).NET支持對象序列化的幾種方式 二進制序列化:對象序列化之後是二進制形式的,通過BinaryFormatter類來實現的,這個類位於System.Runtime.Seri

C#語言-08.序列序列

clas 本質 cnblogs 語法 信息 字段 使用 serializa col a. 序列化:是將對象的狀態存儲到特定存儲介質中的過程 i. 語法:public void Serialize(序列化過程的文件流,保存的對象)

如何使用 JSON for Modern C++ 序列序列號,換行符導致序列失敗

http gb2 elf ldp c++ spi ssp 使用 b16 W48z士5742i4鍁鉀http://shequ.docin.com/qbvl66336 6j夠裝何掖tj嘔蔡5VLhttp://tushu.docin.com/olybb166 渤k搶97L巳綱鋅7

【轉】C# 序列序列

使用 ria tle 輸入 == 必須 mls zab ddr 轉自:https://www.cnblogs.com/lsy131479/p/8371858.html 對象持久化到文本文件,策略是:將對象的屬性值打散,拆解,分別存儲。 序列化: 保存對象的"全景圖" 序

C#序列序列

color bsp private start sys bin create body null 一、序列化: 1 public void SerilizeData() 2 { 3 FileStream fs =

C# 使用 protobuf 進行對象序列序列

member 開源項目 serial all 序列化與反序列化 ace ogl serialize dll protobuf 是 google的一個開源項目,可用於以下兩種用途: (1)數據的存儲(序列化和反序列化),類似於xml、json等; (2)制作網絡通信協議。

C#使用JavaScriptSerializer類實現序列序列得到JSON

JavaScriptSerializer類由非同步通訊層內部使用,用於序列化和反序列化在瀏覽器和Web伺服器之間傳遞的資料。 1、新增引用 專案新增:System.Web.Extensions.dll的引用; 程式碼新增:using System.Web.Script.Serializa

c# 通過json.net中的JsonConverter進行自定義序列序列

iter 希望 生成 ade json.net .json implement else col   相信大家在工作中會經常遇見對json進行序列化與反序列化吧,但通常的序列化與反序列化中的json結構與c#中的類模型結構是相對應的,我們是否可以在序列化一個對象時候,讓我們

FastjsonJava高效能JSON庫,序列序列

Fastjson是一個Java語言編寫的高效能功能完善的JSON庫。它採用一種“假定有序快速匹配”的演算法,把JSON Parse的效能提升到極致,是目前Java語言中最快的JSON庫。Fastjson介面簡單易用,已經被廣泛使用在快取序列化、協議互動、Web輸出、And

c# 序列序列

  序列化又稱序列化,是.NET執行時環境用來支援使用者定義型別的流化的機制。其目的是以某種儲存形成使自定義物件持久化,或者將這種物件從一個地方傳輸到另一個地方。    .NET框架提供了兩種序列化的方式:1、是使用BinaryFormatter進行序列化;2、使用SoapF

分散式系統中的序列序列

1.定義以及相關概念 作者 劉丁 釋出於 2015年5月7日 | 3 討論 分享到: 微博 微信 Facebook Twitter 有道雲筆記 郵件分享 稍後閱讀我的閱讀清單 簡介 文章作者服務於美團推薦與個性化組,該組致力於為美團使用者提供每天bi

分散式基礎之序列序列

  目錄 序列化與反序列化,Why? 序列化的意義 一個簡單例項 JAVA序列化的高階認識 serialVersionUID 的作用 靜態變數序列化 父類的序列化 Transient 關鍵字 序列化的儲存規則 序列化實現深克隆 常見

【轉】c#--json序列序列

原文連結http://blog.csdn.net/gf771115/article/details/27114257 建立類 public class Person { private string name; public st

UnityC#的序列序列

序列化操作在我們的開發中使用的十分普遍,本文記錄了公司最近分享會上關於這部分的講解,希望能幫助大家對序列化有更系統的瞭解。 概念 序列化又稱序列化,是.NET執行時環境用來支援使用者定義型別的流化的機制。其目的是以某種儲存形式使自定義物件持久化,或者將這種物件從一個地方傳輸到另一個地方。

C# JSON字串序列序列

using System.Runtime.Serialization.Json; using System.Runtime.Serialization; 二、C#將物件序列化成JSON字串 publicstring GetJsonString()   {       List<Product>

C#序列序列以及深拷貝淺拷貝

基於二進位制資料流的序列化和反序列化 /// <summary> /// 序列化 /// </summary> /// <typeparam name="T"></typeparam>