程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 服務框架演變過程概述

服務框架演變過程概述

編輯:關於JAVA
 

我們的服務框架已經持續做了三年了,在廠內廣泛的使用,目前部署在服務框架上的服務為2k+,每天經過服務框架的服務執行次數為120億+,摸高到150億+,三年的發展並非一帆風順,由於經驗的原因,還是摔了不少跤的,在這篇blog中,來給大家分享下,希望能夠給要做服務框架或服務化的同學帶來一些幫助,少走一些彎路,不一定要一開始就做成完整的服務框架,但至少先做好鋪墊,避免在廣泛使用後再來挽救。

我們的服務框架主要由四個部分的功能組成:
1、標准的服務的交互方式
2、高性能網絡通信
3、軟件負載均衡
4、服務治理
這四個部分都經歷了一些演變,才形成了最後的結構,尤其是服務治理這塊,在各種SOA的文章中,都會說到服務治理這塊,但基本都不會說服務治理到底要做什麼,我們是在經歷過很多後,終於讓服務治理這塊由一堆實際的功能組成,下面分別來看看以上這四塊的演變過程。

標准的服務的交互方式
在服務框架的第一個版本中,最早我們希望對應用完全不侵入,做到應用不需要依賴服務框架的任何包,也不需要在應用裡面定義服務,因此策略是在應用的外部寫一個xml文件,用來描述對外提供的服務,或者要調用的服務,這個版本提供給應用方使用後,發現使用起來非常復雜,因為這個xml文件和應用是不在一起的,一方面不知道該放在代碼倉庫的什麼地方;另一方面對於部署維護而言也是非常的麻煩。
於是決定改進這塊,廠內應用基本都是基於spring的,因此還是決定提供一個類來方便直接在spring bean的xml中發布服務和配置調用服務的代理bean,在這樣的機制下發布服務和調用服務的配置大致就如下了:
<bean class="發布服務的類" or class="調用服務的proxy類">


在改造成這種方式後,應用方使用起來就比較透明和方便了,並且也只是在運行時對服務框架會產生依賴,後來也就基本沒變過了,兄弟廠有改造成<...:service>這樣的方式的,用起來更簡單一點,不過需要加schema,所以我們這邊還是沒去這麼做。
在服務的定義上碰到的主要需求是最早我們只支持一種通信協議以及只支持接口級的超時配置,後續隨著需求調整了一次服務的定義,增加了方法級超時的配置以及通信協議的配置。

高性能網絡通信
在第一個版本中,我們選擇了基於JBoss Remoting來實現,在最初一個訪問量不大的應用中上線時表現挺正常的,可惜的是在後面一個訪問量較大的應用上線後出現了問題,最後查證原因主要是出在了超時設置上,當時采用了默認的60秒超時,剛好當時提供服務的應用出現了處理慢的現象,導致前端的web應用被拖的支撐不住,而在當時的JBoss Remoting版本中,其實這個超時設置是有bug的,因此如果要修復就必須直接修改JBoss Remoting的代碼。
另外一個問題是JBoss Remoting的連接池方式,在通過硬件負載設備訪問提供服務的集群的情況下,一旦重啟,會很容易出現連接嚴重不均衡的現象。
鑒於上面的現象,覺得還是自己掌握整個通信過程比較靠譜,於是選擇了基於Mina來實現整個網絡通信,自行實現異步轉同步、超時、連接管理,連接的使用采用了每目標地址單個連接的方式,這樣一方面是可以避免連接池造成的連接不均衡的問題,另一方面也避免調用端建立太多連接,導致服務提供者連接會不夠用,伸縮性差的問題,另外,由於都是內網的請求,因此采用長連接方式。
基本上一直以來都沒對上面的網絡通信機制做過調整,不過單連接在序列化/反序列化對象消耗時間較長時,會影響到其他的請求或響應,這個一方面是由於Mina的實現機制,另一方面是要結合應用場景來做適當的處理。
在序列化上我們支持了默認的Java和Hessian兩種,但杯具的是當時的Hessian版本較老,並且Hessian新版本與舊版本的兼容做的很差,導致後來我們一直很難升級Hessian的版本。
在網絡通信上,我們經歷過的教訓主要是最早的通信協議上沒帶版本號,導致升級時的兼容性比較難處理;還有就是在設計之初我們是不考慮用於跨語言場景的,但後面跨語言的場景出現了,於是就很被動了;還有一點是如果發送的對象過大或過多造成內存不夠的現象,最早的時候沒做限制。

軟件負載均衡
在最初的幾個版本中,我們都是通過硬件負載設備來訪問服務提供者集群的,但有一次出現過硬件負載設備出故障的現象,導致應用出現故障,但又沒辦法修復,只能等待硬件負載設備問題的解決,在這種情況下,我們覺得隨著服務越來越多,請求量越來越大,中間的硬件負載設備很容易成為最大的風險,於是決定自己做軟件負載均衡。
我們對於軟件負載均衡最重要的一點要求是,當服務調用者調用服務時,是直接和服務提供者交互的,不需要通過任何中間的機器,當時考察了下現有的,覺得只能自己做了,於是我們就基於這個要求實現了自己的軟件負載均衡,不過我們實現軟件負載均衡的方法隨著機器數越來越多,也碰到了不少的挑戰,由於這個涉及實現機制,就不在這裡細說了。
最早在選址上我們僅支持隨機選擇,但由於集群中會出現機器配置相差比較遠的現象,因此決定加入權重的支持,以便分配不同的流量。
一個應用通常會對外提供多種不同功能的服務,這些服務對資源的消耗以及對整體系統的重要性是不一樣的,而服務框架對於所有服務的執行都是放在同一線程池中的,因此會出現某些不重要又耗資源或執行慢的服務把線程池占滿,導致重要的服務無法執行的現象,對於這種現象,我們提供了兩種方案來進行解決:一是分線程池,二是按接口將集群再進行劃分。
分線程池的方法有些時候仍然是不夠的,例如耗資源或執行慢的服務有可能同時把共享的資源消耗完了,那這個時候即使分線程池也仍然會導致重要的服務出問題的現象。
按接口將集群再進行劃分的好處就很明顯了,在不改變代碼結構的基礎上,在軟件負載均衡上實現按接口的路由即可,於是我們實現了這個機制。
隨著按接口路由實現後,又碰到了一個接口中不同的方法的重要性、執行速度以及消耗資源不同的問題,於是相繼我們又支持了按方法以及按參數的路由,這個時候我們的不經過中間節點調用的軟件負載均衡機制發揮出了巨大的優勢,如果是硬件負載設備,一方面是沒辦法做到這樣的7層路由,另一方面硬件負載設備一旦開啟7層路由,性能下降會非常明顯,而在我們的軟件負載均衡實現機制下,則完全不會有這樣的問題。  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved