Java 跨語言實現方案

背景:

在大型分佈式 java 應用中,爲了方便開發者,通常底層的 rpc 框架都會做一些調用的封裝,讓應用層開發人員在開發服務的時候只用編寫簡單的 pojo 對象就可以了,如流行的 spring remoting , jboss remoting 等等,都有這樣的效果。

隨着業務的需要,可能上層應用希望採用非 java 技術,如 php , ruby on rails ,而由於 java gc 和內存模型的限制,可能有的底層服務又需要採用更高性能和更加靈活的技術,如果 c++ , python 等。

這時候就會考慮跨語言的問題了,在如何不改動原有 pojo 實現的 rpc 框架,而讓系統實現跨語言,這個難題擺在了中間件開發者的頭上。

問題 :

現在我們不妨把上面說涉及的問題提取出來:

1)  不能改變原有的 java rpc 服務的發佈方式,仍然採用 pojo 。

2)  上層非 java 應用可以調用到由 server 端 pojo 形式發佈的服務。

3)  底層非 java 應用,如 c++ , python 等可以發佈格式和 pojo service 一樣的服務

4)  提供優雅的藉口給應用開發者。

業界考察:

好在我們並不是第一個遇到這個問題的人,那我們來看看在我們業界的前輩們都給我們留下了哪些寶貴的財富(主要是互聯網行業)。

Google protocol buffers : Google 大神總是早人一步,在 google 架構的初期就意識到了跨語言的重要性,在構建 bigtable , GFS 的同一時期就是定製出了一套跨語言方案。那就是 google protocol buffers ,不過直到 08 年, google protocl buffers 纔開源出來,正所謂國之利器不可以示人,我們所看到的, google protocl buffers 其實是閹割版,如沒有 map 的支持 ( 根據一些資料表明, google 內部是有這個東西的) , python 的 native c 性能優化,不包括 rpc service ,雖然後面補了一個,但是可用性差強人意,不能多參,不能拋異常。不過在這方面我們確實不應該報太大的希望,因爲 google 自己都說了 protocol buffers – a language-neutral, platform-neutral, extensible way of serializing structured data ,好吧,他只是一個序列化格式,而和 hessian , java 序列化有所不同的是, protocol buffers 可以用通過定義好數據結構的 proto ( IDL )文件產生目標語言代碼,大大了減少了開發量,不過遺憾的是生成的代碼有很強的侵入性,並不能產生我們需要的pojo java 對象。

不過即使是這樣,我們也從 google  protocol buffers 身上學到了很多東西。

  1. 編碼的壓縮,採用 Base 128 Varints 序列化數字,減少網絡傳輸開銷。
  2. 非自描述數據, protocol buffers 將每個數據結構的描述信息嵌入到代碼中,因此只需要傳輸數據過來,就可以反序列化出來該數據結構的實例了。
  3. Immutable object , protocol buffers 在生成的 java 代碼中採用 builder&message 模式, message 是一個不能變的對象,即只有getter ,沒有 setter ,而每一個 message 的生成由一個對應的 builder 來完成,從這點可以看出, google 已經用上了函數式編程。
  4. Rpc 異步話,雖然 protocol buffers 的 rpc 很簡陋,但是一開始就只提供異步 callback 調用形式,可見 google 已經實現異步話,如果在互聯網行業的人會知道,這點是相當不容易。

Facebook thrift : 4 月 1 號,呵呵,沒錯, thrift 是 Facebook 於 07 年愚人節開源出來的,有點 google 的作風。 Thrift 是facebook 自己的一套跨語言實現。有人會問這個和 protocol buffers 有啥區別。 Ok ,先看看它的定義吧。

Thrift is a software framework for scalable cross-language services development. It combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, and OCaml.

說得很清楚是一個跨語言的服務開發框架。包括的功能有 code generation (代碼生成, protocol buffers 也有), cross-language (跨語言, protocol buffers 也有), service development (好吧,這個 protocol buffers 也有)。暈倒,這樣看起來,它和 google protocol buffers 完全是同一個領域的東西,而其有點重複發明輪子的味道。

剛開始,我們也有這樣一個疑惑,好吧,接着往下看, here we go 。其實除了這些共同性以外(都是解決跨語言問題嘛), thrift 還是和protocol buffers 有很大不同的。不同點如下:

1)  提供一個完整的 service stack ,定義了一整套的 rpc 服務框架棧,這個 protocol buffers 是沒有,這個絕對是 thrift 的利器,如果你想要開發一個服務, thrift 甚至有個棧層的實現,我靠,爽。

2)  Ok ,在 thrift 論文有這樣一句話。 Thrift enforces a certain messaging structure when transporting data, but it is agnostic to the protocol encoding in use. 嗯哼,我懂了,它是不會管,你到底採用哪種序列化方式的,hessian ,xml 甚至是protocol buffers 。Oh ,my god 。

3)  接下來不得不膜拜一下thrift 的service 接口的強大了,多參,異常,同步,異步調用的支持,這正是我們想要的, 瞬間給protocol buffers 比下去了。

4)  多集合的支持 map , set 都有,讓你爽歪歪。 Protocol buffers 顫抖吧。

這時候我們親愛的讀者就會問了,那我們的問題不就解決了嗎,就是 thrift 。我笑而不語 , 雖然 thrift 是如此的強大,但是它仍然不是我們想要的, thrift 生成的代碼也是強侵入性的,這樣 pojo 的對象是無法發佈服務的。還有一個硬傷是雖然 thrift 的 stack 很強大,當時這和我們原有系統的 stack 肯定是不兼容的,如 jboss remoting , spring remoting ,它們都會加一些 header 信息,而 thrift 已有實現的傳輸中式沒有header 信息的。值得一提的是現有的 thrift service 實現中,不是線程安全的,考慮到有些語言沒有對線程很好的支持,尤其是 Facebook 最常用的 PHP 語言,所以現有的實現中沒有線程安全 Client 的實現。這樣就會造成 client 端 connection 不能複用的問題,相當於短連接了。( ps :其實短連接就真的比長連接性能差嗎?這是個問題。)

總結一下從 Facebook thrift 學到的東西:

1)              同步,異步都支持,這個很強悍,一般的做法是對性能要求高的服務器端採用異步方式開發,對易用性有要求的客戶端採用同步方式調用,是比較完美的。

2)              從現有的非線程安全的實現看, Facebook 很有可能自己有一套更高效的線程安全的實現,估計考慮到和 thrift 關係不到,或者是核心技術,所以沒有放出來,其實想自己做,也不是太難。

3)              Thrift 對很多腳本語言都進行了 native c 的性能優化,如 python 端,採用 native c 以後性能提高 20 倍。 Protocol buffers 一直在做這方面的優化,打算在 2.4 中加入,不過 protocol buffers 就像 jdk 7 一樣難產,跟讓人崩潰的是,前不久在論壇爆出做這塊優化的哥們已經離開了 google ,不再負責了,好吧,我關心的是他去哪兒了,淚奔。

Apache Hadoop avro : Avro is a data serialization system. Avro provides functionality similar to systems such as Thrift, Protocol Buffers, etc. 好吧它自己都承認了,我們就不去糾結了。

簡單介紹一下, avo 是 hadoop 項目下面用來傳輸數據的一個架構。也是一個跨語言解決方案。不過 avro 有自己的亮點。 1 , Dynamic typing, 2 , Untagged data , 3 , . No manually-assigned field Ids

眼前一亮, Dynamic typing , oh , my god 。沒錯, avro 通過將 metadata 放在一個叫 schema 的對象裏面,然後可以序列化對應的 pojo兌現。這個正是我想要的,至於其他的特性,的確沒有咋仔細看 avro ,感覺上比 thrift ,和 protocol buffers 跟難學習,有熟悉的讀者可以給我科普一下。

解決方案:

好了,到了這裏,讀者大概心裏也有數了, protocol buffers , thrift , avro 都有我們想要的和我們不想要的。要解決我們的問題,我們只需要揚長避短就可以了。揉揉就是我們的東西了。方案如下:

1)  採用 protocol buffers 的 message 序列化格式和代碼生成。

2)  採用 thrift 的 service 生成格式,以及實現兼容 jboss remoting 或者 spring remoting 的 thrift ( jboss remoting ) stack 。

3)  原有的 pojo 對象採用 avro 的 schema 方式序列化和反序列化該對象。

Ok 了,一切看起來是那樣的完美。呵呵,不要被迷惑,還有很多 detail 的事情需要解決,時候不早,吃碗泡麪,洗洗睡了,有時間,再把具體實現 detail 分享給大家。


轉載:http://rdc.taobao.com/team/jm/archives/389

發佈了34 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章