Protocol Buffers的基礎說明和使用,protocolbuffers
我們開始需要使用protobuf的原因是,在處理OJ的contest模組的時候,碰到一個問題就是產生contestRank的時候,需要儲存很多資訊。如果我們採用model儲存的話,那麼一方面後續如果繼續加入其他資訊的話修改會灰常麻煩,另一方面就是實現比較複雜,因為對於rank來說,每一條rank的主鍵首先是UserID,其次儲存的基本資料有AC數,題目AC情況,罰時等等,其中題目AC情況又包括以題目ID為主鍵,屬性(是否AC,AC時間,罰時,這道題目的提交統計),而每一道題目的提交統計又是一個submit實體,這樣的話,就會有多級嵌套存在。如果後續添加的話會更多。還有一個原因就是儲存也不方便,邏輯構思不是很明確,需要注意儲存各種資訊,實現複雜。
protobuf的主要機理是把儲存的資訊序列化為一個字串儲存,需要資訊的時候又可以逆序列化把原始資訊還原出來,而且可以實現多級嵌套的屬性。所以就嘗試使用這個來解決rank的儲存(/ □ \)
1:protobuf是什嗎?
好吧,這個有百科,see see:protocolbuffer是google 的一種資料交換的格式,它獨立於語言,獨立於平台。google 提供了三種語言的實現:java、c++ 和 python,每一種實現都包含了相應語言的編譯器以及庫檔案。由於它是一種二進位的格式,比使用 xml 進行資料交換快許多。可以把它用於分布式應用之間的資料通訊或者異構環境下的資料交換。作為一種效率和相容性都很優秀的位元據傳輸格式,可以用於諸如網路傳輸、設定檔、資料存放區等諸多領域。
簡單地說,就是把某種資料結構的資訊,以某種格式儲存起來。主要用於資料存放區、傳輸協議格式等場合。
2:下載安裝:
地址:http://code.google.com/p/protobuf/downloads/list
安裝:
tar -xzf protobuf-2.1.0.tar.gz cd protobuf-2.1.0 ./configure make make check make install
3:使用建立
網上有各種例子,比如簡單的book的書寫等等都可以用來練習操作
首先,首先我們需要編寫一個 proto 檔案,定義我們程式中需要處理的結構化資料,在 protobuf 的術語中,結構
化資料被稱為 Message。例如:
package contest_submit; message ContestSubmit { required int32 id = 1; required int64 timestamp = 2; }
上述例子中id是int32的,timestamp是int64的。required代表這個是必須的,有點像資料庫裡的not null,還有一
種就是optional,即可選的。
編譯.proto檔案,命令如下:
protoc -I=./ --python_out=./ ./*.proto
代表的意思是輸入的.proto檔案在目前的目錄,輸出產生的編譯檔案到目前的目錄,源檔案是所有以.proto結尾的檔案
。網上完整的格式是:
假設您的 proto 檔案存放在 $SRC_DIR 下面,您也想把產生的檔案放在同一個目錄下,則可以使用如下命令: protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/檔案明.proto
編譯完成之後會產生 contest_submit_pb2.py檔案(對於上面的例子來說)
當然,protobuf也是可以嵌套的。即可以在一個類中聲明另一個類的對象,例如,rank的實現:
package contest_rank; message Submit { optional string status = 1; optional string date_time = 2; optional string runID = 3; } message Problem { optional int32 problemID = 1; repeated Submit submit = 2; optional int32 acindex = 3; optional int32 totalindex = 4; optional int32 time = 5; optional int32 FB = 6; } message Rank { optional string userID = 1; optional string contestID = 2; repeated Problem problem = 3; optional int32 penalty = 4; optional int32 ac = 5; optional int32 total = 6; } message ContestRankList { optional string contestID = 1; repeated Rank rank = 2; }
這樣的話,就可以實現我們前面的需求即多級資訊嵌套了。
使用:
首先,要記得載入標頭檔from 檔案夾.proto import rank_pb2
對於實現rank的儲存的時候,首先聲明一個rank類,以便後續對資料的處理
class Rank(): class Problem(): class Submit(): def __init__(self, runID = "", status="", date_time=""): self.status = status self.date_time = date_time self.runID = runID def __init__(self, problemID): self.problemID = problemID self.submit_list = [] self.acindex = 0 self.totalindex = 0 self.time = 0 self.FB = 0 def add_submit(self, submit): self.submit_list.append(submit) def __init__(self, userID, contestID): self.userID = userID self.contestID = contestID self.problem_list = {} self.ac = 0 self.penalty = 0 self.total = 0
當我們得到rank_list的時候(rank_list是一個存放rank的字典),首先聲明一個proto的載體:
contest_rank_list = rank_pb2.ContestRankList()
然後填充資訊,比如先把比賽的ID加入,即contest_rank_list.contestID = contestID。然後按照protobuf的結構,依
次把需要的資訊填入。對於嵌套的實體來說,每次聲明的時候可以使用add()方法,比如在一個比賽中,會有多個使用者
的rank資訊存在,所以按照使用者ID來添加資訊:
rank_proto = contest_rank_list.rank.add()rank.load_data_to_proto(rank_proto)
其中的load_data_to_proto是rank類的一個方法,內容就是把資訊加入到protobuf裡面而已:
def load_data_to_proto(self, rank): rank.userID = self.userID rank.contestID = self.contestID rank.ac = self.ac rank.penalty = self.penalty rank.total = self.total for problemID in self.problem_list: problem = self.problem_list[problemID] p = rank.problem.add() p.problemID = problem.problemID p.acindex = problem.acindex p.totalindex = problem.totalindex p.time = problem.time p.FB = problem.FB for submit in problem.submit_list: s = p.submit.add() s.status = submit.status s.date_time = submit.date_time s.runID = submit.runID return rank
這樣我們就把資料存入了我們聲明的protobuf裡面了,然後就是儲存的時候序列化,比如我們儲存到SSDB裡面即:
ssdb_api.SetContestRankListProto(contestID, contest_rank_list.SerializeToString())
至此,資訊已經存入資料庫,那麼接下來就是如何讀出了:
對於得到我們儲存的rank資訊來說,即:
contest_rank_list = ssdb_api.GetContestRankListProto(contest_id) rank_list = rank_pb2.ContestRankList() rank_list.ParseFromString(contest_rank_list)
即,再次聲明一個protobuf的對象,然後逆序列化,就得到了我們需要的資訊,簡單吧(/ □ \)。
後面需要的資料,就用父名.子名訪問即可。