新聞中心
大家好,我是了不起。

公司主營(yíng)業(yè)務(wù):成都做網(wǎng)站、成都網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶(hù)真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶(hù)帶來(lái)驚喜。創(chuàng)新互聯(lián)推出龍灣免費(fèi)做網(wǎng)站回饋大家。
最近我們?cè)陧?xiàng)目中,通過(guò)使用 protobuf 格式作為存儲(chǔ)數(shù)據(jù)的一個(gè)載體。一個(gè)不小心就給自己埋了個(gè)大坑,還是過(guò)了好久才發(fā)現(xiàn)。
protobuf 簡(jiǎn)介
protobuf 全名叫 Protocal buffers. 它是由 Google 研發(fā)的,一種可跨語(yǔ)言、可跨平臺(tái)、可擴(kuò)展的序列化數(shù)據(jù)的機(jī)制。類(lèi)似于 XML ,但是它更小、更快、更簡(jiǎn)單。你只需要定義一次你希望的數(shù)據(jù)如何被結(jié)構(gòu)化,然后你可以使用它的生成工具,生成包含一些序列化和反序列化等操作的源代碼??梢暂p松地從各種數(shù)據(jù)流和使用各種編程語(yǔ)言寫(xiě)入和讀取結(jié)構(gòu)化的數(shù)據(jù)。
proto2版本支持在Java、Python、Objective-C和C++中生成代碼。使用新的proto3語(yǔ)言版本,你還可以使用Kotlin、Dart、Go、Ruby、PHP和C#,還有更多的語(yǔ)言。
怎么發(fā)現(xiàn)的?
在我們的新項(xiàng)目中,我們通過(guò)使用 protobuf 格式來(lái)存儲(chǔ)項(xiàng)目運(yùn)行的數(shù)據(jù)。這樣我們?cè)谡{(diào)試過(guò)程中,可能根據(jù)現(xiàn)場(chǎng)錄制的數(shù)據(jù)進(jìn)行本地的調(diào)試。
message ImageData {
// ms
int64 timestamp = 1;
int32 id = 2;
Data mat = 3;
}
message PointCloud {
// ms
int64 timestamp = 1;
int32 id = 2;
PointData pointcloud = 3;
}
message State {
// ms
int64 timestamp = 1;
string direction = 2;
}
message Sensor {
repeated PointCloud point_data = 1;
repeated ImageData image_data = 2;
repeated State vehicle_data = 3;
}我們定義了這樣一組數(shù)據(jù), 然后存儲(chǔ)的時(shí)候,因?yàn)镾ensor 這3個(gè)數(shù)據(jù)源的幀率不一樣,因此存儲(chǔ)的時(shí)候,單個(gè) Sensor 中其實(shí)只包含了一組數(shù)據(jù),另外兩個(gè)類(lèi)型的數(shù)據(jù)并沒(méi)有包含進(jìn)去。
當(dāng)我們只錄制單個(gè) pack 的時(shí)候,我們并沒(méi)有遇到問(wèn)題。直到我們覺(jué)得單個(gè)包,不能長(zhǎng)時(shí)間錄制,我們需要找一種解決方法來(lái)分割包 。
當(dāng)時(shí)覺(jué)得這個(gè)一定是很簡(jiǎn)單的,我們就設(shè)定了一個(gè)包達(dá)到 500M 的時(shí)候,我們就讓后面的數(shù)據(jù)存到新的包中。很順利的寫(xiě)完,然后放到現(xiàn)場(chǎng)進(jìn)行數(shù)據(jù)錄制。錄制一段時(shí)間之后,我們把包拿回來(lái)進(jìn)行模擬測(cè)試我們的新程序。發(fā)現(xiàn)有些包的數(shù)據(jù)解析出來(lái)是有問(wèn)題的。程序運(yùn)行到一半會(huì)卡在那里不動(dòng)。經(jīng)過(guò)多次測(cè)試,發(fā)現(xiàn)是部分包有這個(gè)問(wèn)題。
我們一開(kāi)始懷疑的是,判斷文件大小的方式不對(duì),影響到了分包。因?yàn)榕袛辔募笮〉臅r(shí)候,會(huì)去打開(kāi)文件。但是經(jīng)過(guò)好幾種其他的不打開(kāi)文件的方式判斷,從而進(jìn)行分割。還是遇到了部分錄制的包有問(wèn)題。
這時(shí)我才懷疑到 protobuf 對(duì)存儲(chǔ)數(shù)據(jù)會(huì)有一些特殊的要求。后來(lái)看了一些文章,了解到 protobuf 存儲(chǔ)多組數(shù)據(jù)到一個(gè)文件需要有標(biāo)志符。要不然后面從文件解析回來(lái)的時(shí)候,protobuf 因?yàn)椴恢绬蝹€(gè)數(shù)據(jù)的停止符在哪里,導(dǎo)致數(shù)據(jù)解析出錯(cuò)。
到這里,這個(gè)坑出現(xiàn)了。我們存儲(chǔ)了一系列的數(shù)據(jù)到單個(gè)包中,沒(méi)有做任何分隔符的操作。protobuf在解析的時(shí)候,把文件中所有的內(nèi)容都解析成了單個(gè)Sensor。Sensor 中包含里所有數(shù)據(jù), protobuf 主動(dòng)合并了所有存儲(chǔ)的數(shù)據(jù)。
在這時(shí),我才發(fā)現(xiàn)以前單包錄制的時(shí)候,數(shù)據(jù)都是對(duì)的,那真的是我運(yùn)氣好。protobuf恰好解析成功了。
怎么解決呢?
既然知道 protobuf 會(huì)這么操作,那我們就只要知道 protobuf 怎么分割就行了。這個(gè)方法還真不好找,因?yàn)橄裎覀冞@樣使用的人太少了。中文搜索完全搜不到這一塊的內(nèi)容,可能大家都不會(huì)使用protobuf來(lái)存儲(chǔ)數(shù)據(jù)吧,大家使用的方式應(yīng)該都是多個(gè)服務(wù)中進(jìn)行交互的場(chǎng)景吧。
最終通過(guò)stackoverflow上的一些回答找到了答案,從回答中得知,這個(gè)解決辦法在 protobuf 3.3 的時(shí)候,才正式被合并進(jìn)去??雌饋?lái)這個(gè)功能真的很少用啊。
bool SerializeDelimitedToOstream(const MessageLite& message,
std::ostream* output);
bool ParseDelimitedFromZeroCopyStream(
MessageLite* message, io::ZeroCopyInputStream* input, bool* clean_eof);
通過(guò)這一對(duì)方法,可以對(duì)文件進(jìn)行按照數(shù)據(jù)流一個(gè)一個(gè)的存儲(chǔ)讀取。再也不用擔(dān)心數(shù)據(jù)被合并讀取。
當(dāng)然通過(guò)這種方式存儲(chǔ)的數(shù)據(jù),不能被原來(lái)的解析方式所解析,存儲(chǔ)的而進(jìn)行格式完全變了。這種方式會(huì)先存儲(chǔ)二進(jìn)制數(shù)據(jù)的大小,再存儲(chǔ)二進(jìn)制數(shù)據(jù)。
結(jié)束語(yǔ)
經(jīng)過(guò)一番折騰,終于搞定了這個(gè)分割的坑。使用場(chǎng)景可能比較小眾,導(dǎo)致了很多資料根本找不到。靠自己看源碼才發(fā)現(xiàn)這些問(wèn)題。C++ 的源碼真不好讀,有很多的模板方法、模板類(lèi)容易錯(cuò)過(guò)一些細(xì)節(jié)。最后還是看的C#的代碼,才完全確認(rèn)的。
網(wǎng)站欄目:一個(gè)bug竟然是Protobuf的feature
標(biāo)題網(wǎng)址:http://m.fisionsoft.com.cn/article/dpjocpp.html


咨詢(xún)
建站咨詢(xún)
