序列化: 一個老家伙的咸魚翻身
1.寒冬的蟄伏
這里的工作很繁忙,一年365天, 一天24小時幾乎不停工。
但是我卻是一個閑人, 因為我做的工作最近用的人太少了, 經(jīng)常被冷落在一邊。
大多數(shù)時候,我只能羨慕的看著線程、反射、注解、集合、泛型這些明星員工在那里忙忙碌碌, 聽著他們充滿激情的的大聲說笑。
他們都叫我序列化,想想也是, 我的工作就是把一個Java 對象變成二進制的字節(jié)流, 或者反過來把字節(jié)流變成Java 對象, 這有什么意思?
當大家需要一個Java 對象的時候, 直接new 出來不就得了, 對象不用了自然有令人膽戰(zhàn)心驚的垃圾回收去處理。
但是存在即合理, 在JDk1.1的時代, 我就已經(jīng)存在了。 當時人們的思想很超前: 網(wǎng)絡就是計算機。 一個個Java 對象應該可以在網(wǎng)絡中到處旅行 : 從一個機器出發(fā)時,就變成二進制字節(jié)流,順著網(wǎng)絡跨過千山萬水, 到達另外一臺機器,在那里搖身一變,恢復成Java 對象, 在那里繼續(xù)運算。
既然可以以二進制方式在網(wǎng)絡中漫游, 那自然也可以把這些字節(jié)流存到硬盤中, 當JVM停機,整個世界坍塌以后, 線程,反射,注解都不復存在了, 而我的字節(jié)流還會在硬盤上默默等待, 等待下一次JVM的重生, 把對象恢復。
所以我覺得我的工作也很有價值, 從某種意義上來講, 我可以讓Java 對象跨越時間和空間而永生 !
這種永生是有代價的, 首先你必須得用Java, 這是廢話, 因為我只是java對象序列化。
雖然那二進制字節(jié)流的格式是公開的, 你可以用任何語言(C,C++,Python...)去解析讀取, 但是解析以后又有什么用處呢? 那些字節(jié)流中會告知你這是哪個類的數(shù)據(jù),字段的類型和值, 但是如果你沒有相對應的Java 類,還是無法構建出Java 對象出來。
其次, 做序列化雙方的類必須得一致, 要不然肯定出亂子。
大部分人都不知道在上個世紀末和本世紀初, 我還是隨著J2EE火了一陣, 當時J2EE中有個叫RMI東西, 其實就是Java RPC。 由于我卓越的工作, 開發(fā)人員用可以輕松的調用遠程服務器上的Java 方法, 就相當于調用本地方法一樣, 很方便。
可惜的是這個RMI只能用在Java環(huán)境中,對于服務器來說這根本不是問題, 但是當時Web應用正在興起, 一個瀏覽器中是很難有Java環(huán)境的, 所以RMI很快就沒落了, 我也隨之被打入冷宮, 我也只好蟄伏下來,等待機會。
2.XML和JSON的挑戰(zhàn)
后來我們這里來了一個叫XML的小伙子,很受大家的歡迎, 都喜歡把Java 對象序列化的工作交給他去做。
我不能坐以待斃, 我仔細的觀察了幾天以后, 終于發(fā)現(xiàn)這個家伙有個大缺點: 太復雜了!
對于我的Java 序列化,大部分情況下你只需要讓你的類實現(xiàn)Serializable接口, 我就可以接管后續(xù)的所有工作。不用你操心了。
可是用XML, 你還得寫一堆代碼把一個類中的各個字段和他們的值變成XML標簽/屬性/值 才行。 當用來表示對象的XML字符串漫游到另外一個機器上, 還得有一堆代碼把XML變成對象。
我嘲笑XML說: “小伙子, 你這也太麻煩了吧, 人類的時間多寶貴, 為了用XML做序列化,代價好高嗷!”
“老家伙,沒你想的那么復雜, 你可能不知道, 我們有些類庫能自動幫助把對象變成XML” 他毫不示弱。
“ 不要忘了 ” 小伙子補充道 “ 我們XML可是語言中立的, 在這里是Java對象, 到了客戶端 什么語言都行 , Java/C/Python/Ruby.... 都沒問題, 甚至瀏覽器里的Javascript都能處理, 這一點你不行了吧?”
這家伙戳到了我的痛處, 在瀏覽器中我的確需要一個Java 環(huán)境才行運行 , 唉,真是成也Java ,敗也Java。
我說: “我知道你是語言無關的, 但是你注意到?jīng)]有, 你的XML標簽冗余太多, 真正的數(shù)據(jù)很少。 比如有個Person類, 有兩個字段name和address, 用你的XML做序列化就變成了這個樣子<person><name>abc</name><address>xyz</address></person>, 這在網(wǎng)絡上傳輸起來絕對是一種浪費! 我的java 字節(jié)流就不一樣了, 二進制的,非常緊湊,一點都不浪費!“
XML小伙子沉默了, 小樣, 我也抓住了你的痛點。
過了兩天,這個小伙子又帶來了一個叫JSON的小弟, 他得意洋洋的向我炫耀: 用了JSON以后,數(shù)據(jù)精簡多了, 不信你看:{"name”:“abc", "address":"xyz"} , 現(xiàn)在我們不但語言中立,還很精簡, 老家伙,這下你無話可說了吧。
我認栽, 但是讓XML也沒高興多久, 讓他沒有想到的是, Web時代JSON和Javascript是一對絕配, 聯(lián)手統(tǒng)治了瀏覽器。 連XML自己都快沒飯吃了。
3.新協(xié)議的崛起
其實我一直覺得我的二進制序列化方式能減少存儲空間, 方便網(wǎng)絡傳輸,只是我的硬傷是無法跨越語言。
不行, 我不能一直守著Java這一畝三分地了, 必須擴展支持多語言, 這樣才能脫離Java環(huán)境。
有人說: 計算機的所有問題都可以通過增加一個中間層來解決。 我是不是也可以搞個中間層出來?
讓這個中間層來定義/描述消息的格式,然后再弄一個小翻譯器( 不,叫編譯器顯的更加高大上), 把這個程序員自定義的消息格式轉換成各種語言的實現(xiàn),例如java, python, c++等等。
在轉換好的語言實現(xiàn)里邊,自動包含了要被序列化的類的定義, 以及實現(xiàn)序列化和反序列化的代碼, 當然序列化以后的數(shù)據(jù)是二進制的。
等到二進制的字節(jié)流通過網(wǎng)絡傳輸?shù)搅硗庖慌_機器, 就可以反序列化為各種語言(例如Python)的對象了, 當然必須是同一個消息格式產(chǎn)生的Python類。
不僅僅是Python, C++, Go, C# , 甚至Javascript 都可以用 !
是不是很爽 ? 既語言中立, 又采用二進制傳輸, 體積小,解析快, ***的綜合了各種優(yōu)點!
唯一的額外工作是需要把消息格式的定義編譯成各種語言的實現(xiàn), 為了能支持多語言,這也是沒辦法的事情 。
我得意的把新方案給XML和JSON這兩個家伙看了, 從表情來看,就知道他倆如臨大敵了。
我也把方案提交給了我們服務器世界的老大 , 他大為贊賞, 決定先在部分場景下用起來, 例如對象存入緩存的時候需要序列化, 以前用json, 占用空間很大, 改用了我的新方案以后, 不但減少了空間使用, 還提升了讀寫的效率, 效果不錯。
我成功地扳回了一局,現(xiàn)在找我用新方案來做序列化的人越來越多了, 但是最終鹿死誰手還很難說,最有可能的情況是,各種方案混合使用, 即使是這樣,我也很滿足了。
【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉載請通過作者微信公眾號coderising獲取授權】