Java跨語言調用實現方案

jasonqzy 8年前發布 | 16K 次閱讀 Java Thrift Java開發 protobuf

來自: http://blogread.cn/it/article/7396?f=hot1

背景:

在大型分布式 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 已經實現異步話,如果在互聯網行業的人會知道,這點是相當不容易。

非死book thrift 4 月 1 號,呵呵,沒錯, thrift 是 非死book 于 07 年愚人節開源出來的,有點 google 的作風。 Thrift 是非死book 自己的一套跨語言實現。有人會問這個和 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 實現中,不是線程安全的,考慮到有些語言沒有對線程很好的支持,尤其是 非死book 最常用的 PHP 語言,所以現有的實現中沒有線程安全 Client 的實現。這樣就會造成 client 端 connection 不能復用的問題,相當于短連接了。( ps :其實短連接就真的比長連接性能差嗎?這是個問題。)

總結一下從 非死book thrift 學到的東西:

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

2)              從現有的非線程安全的實現看, 非死book 很有可能自己有一套更高效的線程安全的實現,估計考慮到和 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 分享給大家。

 本文由用戶 jasonqzy 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!