:
在大型分布式 java 應(yīng)用中,為了方便開發(fā)者,通常底層的 rpc 框架都會做一些調(diào)用的封裝,讓應(yīng)用層開發(fā)人員在開發(fā)服務(wù)的時候只用編寫簡單的 pojo 對象就可以了,如流行的 spring remoting , jboss remoting 等等,都有這樣的效果。
隨著業(yè)務(wù)的需要,可能上層應(yīng)用希望采用非 java 技術(shù),如 php , ruby on rails ,而由于 java gc 和內(nèi)存模型的限制,可能有的底層服務(wù)又需要采用更高性能和更加靈活的技術(shù),如果 c++ , python 等。
這時候就會考慮跨語言的問題了,在如何不改動原有 pojo 實現(xiàn)的 rpc 框架,而讓系統(tǒng)實現(xiàn)跨語言,這個難題擺在了中間件開發(fā)者的頭上。
問題 :
現(xiàn)在我們不妨把上面說涉及的問題提取出來:
1) 不能改變原有的 java rpc 服務(wù)的發(fā)布方式,仍然采用 pojo 。
2) 上層非 java 應(yīng)用可以調(diào)用到由 server 端 pojo 形式發(fā)布的服務(wù)。
3) 底層非 java 應(yīng)用,如 c++ , python 等可以發(fā)布格式和 pojo service 一樣的服務(wù)
4) 提供優(yōu)雅的借口給應(yīng)用開發(fā)者。
業(yè)界考察:
好在我們并不是第一個遇到這個問題的人,那我們來看看在我們業(yè)界的前輩們都給我們留下了哪些寶貴的財富(主要是互聯(lián)網(wǎng)行業(yè))。
Google protocol buffers : Google 大神總是早人一步,在 google 架構(gòu)的初期就意識到了跨語言的重要性,在構(gòu)建 bigtable , GFS 的同一時期就是定制出了一套跨語言方案。那就是 google protocol buffers ,不過直到 08 年, google protoclbuffers 才開源出來,正所謂國之利器不可以示人,我們所看到的, google protoclbuffers 其實是閹割版,如沒有 map 的支持 ( 根據(jù)一些資料表明, google 內(nèi)部是有這個東西的) , python 的 native c 性能優(yōu)化,不包括 rpcservice ,雖然后面補了一個,但是可用性差強人意,不能多參,不能拋異常。不過在這方面我們確實不應(yīng)該報太大的希望,因為 google 自己都說了 protocol buffers – a language-neutral, platform-neutral, extensibleway of serializing structureddata ,好吧,他只是一個序列化格式,而和 hessian , java 序列化有所不同的是, protocolbuffers 可以用通過定義好數(shù)據(jù)結(jié)構(gòu)的 proto ( IDL )文件產(chǎn)生目標(biāo)語言代碼,大大了減少了開發(fā)量,不過遺憾的是生成的代碼有很強的侵入性,并不能產(chǎn)生我們需要的pojo java 對象。
不過即使是這樣,我們也從 google protocol buffers 身上學(xué)到了很多東西。
- 編碼的壓縮,采用 Base 128 Varints 序列化數(shù)字,減少網(wǎng)絡(luò)傳輸開銷。
- 非自描述數(shù)據(jù), protocol buffers 將每個數(shù)據(jù)結(jié)構(gòu)的描述信息嵌入到代碼中,因此只需要傳輸數(shù)據(jù)過來,就可以反序列化出來該數(shù)據(jù)結(jié)構(gòu)的實例了。
- Immutable object , protocol buffers 在生成的 java 代碼中采用 builder&message 模式, message 是一個不能變的對象,即只有 getter ,沒有 setter ,而每一個 message 的生成由一個對應(yīng)的 builder 來完成,從這點可以看出, google 已經(jīng) 用上了函數(shù)式編程。
- Rpc 異步話,雖然 protocol buffers 的 rpc 很簡陋,但是一開始就只提供異步 callback 調(diào)用形式,可見 google 已經(jīng)實現(xiàn)異步話,如果在互聯(lián)網(wǎng)行業(yè)的人會知道,這點是相當(dāng)不容易。
Facebook thrift : 4 月 1 號,呵呵,沒錯, thrift 是 Facebook 于 07 年愚人節(jié)開源出來的,有點 google 的作風(fēng)。 Thrift 是facebook 自己的一套跨語言實現(xiàn)。有人會問這個和 protocol buffers 有啥區(qū)別。 Ok ,先看看它的定義吧。
Thrift is a software framework for scalable cross-language servicesdevelopment. It combines a software stack with a code generation engineto build services that work efficiently and seamlessly between C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk,and OCaml.
說得很清楚是一個跨語言的服務(wù)開發(fā)框架。包括的功能有 code generation (代碼生成, protocolbuffers 也有), cross-language (跨語言, protocol buffers 也有), servicedevelopment (好吧,這個 protocol buffers 也有)。暈倒,這樣看起來,它和 google protocolbuffers 完全是同一個領(lǐng)域的東西,而其有點重復(fù)發(fā)明輪子的味道。
剛開始,我們也有這樣一個疑惑,好吧,接著往下看, here we go 。其實除了這些共同性以外(都是解決跨語言問題嘛), thrift 還是和protocol buffers 有很大不同的。不同點如下:
1) 提供一個完整的 service stack ,定義了一整套的 rpc 服務(wù)框架棧,這個 protocol buffers 是沒有,這個絕對是 thrift 的利器,如果你想要開發(fā)一個服務(wù), thrift 甚至有個棧層的實現(xiàn),我靠,爽。
2) Ok ,在 thrift 論文有這樣一句話。 Thrift enforces a certain messagingstructure when transporting data, but it is agnostic to the protocolencoding in use. 嗯哼,我懂了,它是不會管,你到底采用哪種序列化方式的,hessian ,xml 甚至是protocolbuffers 。Oh ,my god 。
3) 接下來不得不膜拜一下thrift 的service 接口的強大了,多參,異常,同步,異步調(diào)用的支持,這正是我們想要的, 瞬間給protocol buffers 比下去了。
4) 多集合的支持 map , set 都有,讓你爽歪歪。 Protocol buffers 顫抖吧。
這時候我們親愛的讀者就會問了,那我們的問題不就解決了嗎,就是 thrift 。我笑而不語 , 雖然 thrift 是如此的強大,但是它仍然不是我們想要的, thrift 生成的代碼也是強侵入性的,這樣 pojo 的對象是無法發(fā)布服務(wù)的。還有一個硬傷是雖然 thrift 的 stack 很強大,當(dāng)時這和我們原有系統(tǒng)的 stack 肯定是不兼容的,如 jboss remoting , springremoting ,它們都會加一些 header 信息,而 thrift 已有實現(xiàn)的傳輸中式?jīng)]有header 信息的。值得一提的是現(xiàn)有的 thriftservice 實現(xiàn)中,不是線程安全的,考慮到有些語言沒有對線程很好的支持,尤其是 Facebook 最常用的 PHP 語言,所以現(xiàn)有的實現(xiàn)中沒有線程安全 Client 的實現(xiàn)。這樣就會造成 client 端 connection 不能復(fù)用的問題,相當(dāng)于短連接了。( ps :其實短連接就真的比長連接性能差嗎?這是個問題。)
總結(jié)一下從 Facebook thrift 學(xué)到的東西:
1) 同步,異步都支持,這個很強悍,一般的做法是對性能要求高的服務(wù)器端采用異步方式開發(fā),對易用性有要求的客戶端采用同步方式調(diào)用,是比較完美的。
2) 從現(xiàn)有的非線程安全的實現(xiàn)看, Facebook 很有可能自己有一套更高效的線程安全的實現(xiàn),估計考慮到和 thrift 關(guān)系不到,或者是核心技術(shù),所以沒有放出來,其實想自己做,也不是太難。
3) Thrift 對很多腳本語言都進(jìn)行了 nativec 的性能優(yōu)化,如 python 端,采用 native c 以后性能提高 20 倍。 Protocolbuffers 一直在做這方面的優(yōu)化,打算在 2.4 中加入,不過 protocol buffers 就像 jdk7 一樣難產(chǎn),跟讓人崩潰的是,前不久在論壇爆出做這塊優(yōu)化的哥們已經(jīng)離開了 google ,不再負(fù)責(zé)了,好吧,我關(guān)心的是他去哪兒了,淚奔。
Apache Hadoop avro : Avro is a dataserialization system. Avro provides functionality similar to systemssuch as Thrift, Protocol Buffers, etc. 好吧它自己都承認(rèn)了,我們就不去糾結(jié)了。
簡單介紹一下, avo 是 hadoop 項目下面用來傳輸數(shù)據(jù)的一個架構(gòu)。也是一個跨語言解決方案。不過 avro 有自己的亮點。 1 , Dynamic typing, 2 , Untagged data , 3 , . No manually-assigned field Ids 。
眼前一亮, Dynamic typing , oh , mygod 。沒錯, avro 通過將 metadata 放在一個叫 schema 的對象里面,然后可以序列化對應(yīng)的 pojo兌現(xiàn)。這個正是我想要的,至于其他的特性,的確沒有咋仔細(xì)看 avro ,感覺上比 thrift ,和 protocolbuffers 跟難學(xué)習(xí),有熟悉的讀者可以給我科普一下。
解決方案:
好了,到了這里,讀者大概心里也有數(shù)了, protocol buffers , thrift , avro 都有我們想要的和我們不想要的。要解決我們的問題,我們只需要揚長避短就可以了。揉揉就是我們的東西了。方案如下:
1) 采用 protocol buffers 的 message 序列化格式和代碼生成。
2) 采用 thrift 的 service 生成格式,以及實現(xiàn)兼容 jboss remoting 或者 spring remoting 的 thrift ( jboss remoting ) stack 。
3) 原有的 pojo 對象采用 avro 的 schema 方式序列化和反序列化該對象。
Ok 了,一切看起來是那樣的完美。呵呵,不要被迷惑,還有很多 detail 的事情需要解決,時候不早,吃碗泡面,洗洗睡了,有時間,再把具體實現(xiàn) detail 分享給大家。
10/24/2010 1:22 AM 小丑魚