架構設計:遠程調用服務架構設計及zookeeper技術詳解(上篇)

1、序言

     Hadoop是一個技術生態圈,zookeeperhadoop生態圈裏一個很是重要的技術,當我研究學習hadoop的相關技術時候,有兩塊知識曾經讓我十分的困惑,一個是hbase,一個就是zookeeperhbase的困惑源自於它在顛覆了我對數據庫建模的理解,而zookeeper的困惑倒是我沒法理解它究竟是幹嗎的。 html

    前不久我結合我瞭解的一種遠程調用服務的設計來幫助我理解zookeeper在實際的生產中運用,該文章的地址是: 前端

    http://www.cnblogs.com/sharpxiajun/p/3297852.html 正則表達式

   其實這篇文章寫完後,我本身感受並非太好,由於寫本文的時候,對遠程調用服務的設計以及zookeeper的理解都不是很到位,可是這篇文章仍是受到了你們很大的關注,被博客園做爲了推薦文章,還有不少網友但願我寫一篇更加詳盡的文章,還有童鞋留言說這個設計方案和淘寶開源的dubbohsf相似。我相信你們的關注就是意味着這個主題是當下技術的熱點,因此今天我要寫一篇主題和上篇文章同樣的博文,這是上篇文章的升級版,無論是遠程調用服務仍是zookeeper我都會給出更加詳盡的講解。不過這裏仍是要說明下,這篇文章裏遠程服務的設計我仍是沒有參照dubbo,由於最近實在太忙,沒有時間研究淘寶的dubbo,可是我但願學習過dubbo的童鞋能夠幫我對比下個人方案和dubbo的區別,區別就會產生新的問題,也會有新的知識須要研究。 spring

   本文是該主題的上篇,主要是講解遠程調用服務的相關知識,下篇則是根據遠程調用服務架構設計中zookeeper的相關應用方法詳細講解關於zookeeper的知識。 數據庫

 

2、遠程調用服務的架構設計總述

    首先咱們要再深刻理解下爲何應用軟件服務裏須要一個遠程調用服務,遠程調用服務解決了軟件設計中的什麼問題,它的架構設計又有什麼理論根據了? 緩存

    我曾寫了一篇關於分佈式網站架構設計的文章,文章地址是: 服務器

    http://www.cnblogs.com/sharpxiajun/archive/2013/05/11/3072798.html 網絡

    在文章開頭我就把這個新的網站架構方案和傳統的企業軟件的B/S架構做了對比,我將一個網站裏提供業務服務的組件抽象爲獨立的服務系統,接收用戶信息的邏輯部分抽象爲前端系統,服務系統和前端系統使用netty這樣的通信組件進行通信,而到了講解遠程調用服務的框架設計時候我將netty通信組件進一步抽象爲一個通信獨立系統及遠程調用服務,這就是爲何要設計遠程調用服務的緣起了,遠程調用服務又會帶來了網站架構的升級,若是傳統的企業B/S架構爲1.0版,我將前端和業務服務端分離爲獨立系統則是2.0版,那麼引入了遠程調用服務網站就是3.0版了,3.0版的架構帶來的好處就是能夠將N多的前端系統和N多的業務服務端系統融爲一個總體,網站的規模會愈來愈大,提供的服務也會愈來愈多,這既避免重複造輪子的問題還使得網站規模愈來愈大。 session

   3.0版本的網站架構帶來了新的網站架構總圖,以下所示: 架構

 

     有了遠程調用服務,咱們能夠作到業務級別的集羣,例如:一個製造企業,通常都會有采購業務,生產業務、銷售業務以及財務業務,按照傳統的思路咱們都會給每一個業務獨立開發一個系統,若是引用了遠程調用服務,咱們能夠將這些業務都作成獨立的服務,這些服務組成業務集羣,而這些服務都是用統一的遠程調用服務做爲操做的入口,換句話說無論什麼樣的服務對於調用者來講都是統一的,這樣前端的調用者能夠作到應用的統一,所謂的應用的統一淘寶網站是最典型的表明,咱們在一個同一的網站裏能夠操做各類不一樣的應用,而不會發生由於應用的不一樣咱們就得從新訪問新的地址或者從新登陸到另一個系統裏作其餘業務的操做。而服務端這邊,徹底能夠擺脫傳統的客戶端和服務端耦合的開發,加強了整個服務端的專業性和穩定性,這樣更易於服務端的擴展性和可維護性。若是服務端之間也須要相互調用也能夠經過遠程調用服務實現,因爲遠程調用服務的統一性,這樣就避免了服務調用之間報文和調用方式的不統一,規範了整個開發的流程。若是遠程調用服務還有負載均衡功能,整個服務集羣就變成了一個私有的雲,因此說遠程調用服務是雲計算的重要組成部分,這個說法一點都不爲過。

    遠程調用服務的理論依據是什麼,這個問題的表述可能有點問題,其實我要講的是遠程調用服務的技術原型就是SOAService-Oriented Architecture),在雲計算出現前,SOA曾一度是IT的技術熱點,雖然以後不少人說中國的SOA作的一點很差,就和早年的DHTML同樣,詬病遠多於讚揚,寫本文時候我在京東里搜索了下SOA,從書籍的出版日期和書籍評價數就能夠看出SOA已經有點無人問津的淒涼了。下面我要簡單介紹下SOASOA的定義:

     SOA是一個軟件架構,它包含四個關鍵概念:應用程序前端、服務、服務庫和服務總線。一個服務包含一個合約、一個或多個接口以及一個實現。

   應用程序前端能夠理解爲我上面所講述的調用者和前端系統,服務庫能夠理解爲服務集羣,這裏還有個服務是什麼呢?服務就是調用者和服務提供者完成某一個特定業務的合約,換句話說就是封裝的業務規則,打個比方,咱們在淘寶去購物,下訂單,付款,查物流,肯定付款這些操做在服務端都有獨立的服務提供,可是從購物這個概念去理解,這些獨立的服務才能構成這個完整的購物行爲,若是其中有地方出了問題,會有相應不一樣的操做,那麼這個就絕對不是調用者簡單調用服務接口的問題,須要更高層次的業務封裝,將上面這些操做封裝爲一個統一的服務,這個就是所謂的服務。最後一個要素服務總線,這就是咱們本文所談的重要主題:遠程調用服務了。

   這裏談談SOA的目的是想起到拋磚引玉的做用,讓那些想深刻研究遠程調用服務的人能夠從SOA的角度理解遠程調用服務,而那些仍是不明白遠程調用是何物的童鞋能夠經過SOA的概念來理解遠程調用服務。

 

3、遠程調用服務技術詳解

     遠程調用服務技術詳解,詳解,嗚嗚~~,這兩個字頗有壓力,我怕有童鞋看了這個標題會覺得我會將整套技術實現方案寫到裏面,這個難度過高了,寫幾萬字估計都說不清楚,再說真的寫的那麼細緻,估計不少人都看不懂了(嘿嘿,我本身也沒有技術實現過哦,這些都是構思,構思哦),因此詳解就是詳解原理。

     下面我將上篇文章的架構圖放進來,你們再仔細看看這張圖:

 

     傳統的服務調用都是服務提供者和服務調用者的直接調用,從架構圖裏咱們看到這裏多了一個遠程調用管理組件,遠程調用管理組件是一個獨立的服務系統,爲了保證該系統的穩定性,它也必定是一個分佈式的系統,可是這個分佈式系統和Web的分佈式系統是徹底不一樣的分佈式系統,傳統Web應用集羣是基於HTTP協議的無狀態的特色設計的,由於每一個HTTP請求都是一個獨立的事務,不一樣請求之間是沒有任何關係的,因此咱們能夠將Web應用部署到不一樣服務器上,請求無論到了那臺服務器,都能正常的給用戶提供相應的服務,可是Web應用的session機制是有狀態的,因此傳統Web集羣都是要有session同步的操做,大型網站每每會把session功能抽象爲獨立的緩存系統,可是這裏的遠程調用管理組件的集羣原理或者說分佈式原理是有別於Web應用集羣分佈式原理的,遠程調用管理組件能夠當作一個註冊中心,它會記錄下服務提供者和服務調用者的相關信息,並將這些信息推送給服務提供者或者服務調用者,爲了保證系統的執行效率,這些註冊信息都是記錄在內存裏,咱們試想下,若是這些註冊信息丟失,整個系統將會不可用,所以遠程調用管理組件的集羣是一種保證數據可靠性和服務提供健壯性的集羣,而不是創建在HTTP無狀態特性基礎上的集羣。咱們這裏假想下遠程調用服務的集羣運行場景,咱們假若有5臺服務器做爲遠程調用服務運行的服務器,那麼每臺服務器都必須有註冊信息的冗餘備份,當服務運行時候其中一臺服務器發生了故障,這臺故障的服務器上的數據不會丟失,此外集羣應該還要有一個檢查故障的機制,當發現有臺服務器不可用的時候,能及時剔除該服務器,而zookeeper就是解決這種問題的技術框架。此外除了保證系統的穩定性和可用性外,集羣的數據存儲方式也是很重要的,前面我講到集羣的數據存儲要有一個冗餘機制,除了冗餘機制還要有一個很適合快速訪問和讀寫的數據模型,而zookeeper正好包含這種數據模型,因此我設計的遠程調用服務是一個很適合zookeeper應用的場景,至於zookeeper的詳細知識我會在下篇裏詳細講到。

     遠程調用管理組件還有一個心跳機制,心跳機制的做用是檢測服務提供者的健康性及服務提供者是否可用,服務提供者啓動時候會將本身的註冊信息發送給遠程調用管理組件,這個註冊信息裏包含服務端的ip地址和端口號,遠程調用管理組件會啓動一個線程,根據定時對這個ip地址和端口號去ping這個ip和端口號對應的應用是否可用,若是不可用遠程調用管理組件會反覆嘗試幾回,這個次數和多久檢測心跳都是能夠配置的,若是反覆幾回仍是不通,那麼就認定該服務不可用了。有網友在QQ上問我,爲何不檢測服務調用者的心跳,這個徹底不必哦,調用者是主動方,提供者是被動方,這就比如你訪問網站,若是你生病了不去訪問了,系統沒有必要檢查你是否已經生病了。

     遠程調用框架須要使用序列化和反序列化技術,這點也讓不少童鞋不太理解,不理解的緣由仍是對序列化和反序列化技術的不理解,序列化技術主要是應用與數據持久化(數據存到硬盤)或者網絡通信,無論是數據存儲到硬盤仍是進行網絡通信,這些數據都會轉化爲二進制,序列化就是將正在運行的對象轉化爲能夠存儲和傳輸的二進制數據,而反序列化是能夠將這些二進制數據反向還原成原來的對象信息,還原的對象仍是能夠被程序操做的,而咱們設計的遠程調用框架傳遞就是不一樣系統之間能夠相互使用的程序代碼,因此咱們須要使用序列化和反序列化技術。這裏就有一個問題,例如咱們傳輸一個對象,這個對象對應的類是N多個類的繼承子類,並且這個對象裏可能還會引用其餘的對象例如StringArrayList等等,那麼爲了讓反序列化的對象可用,序列化的時候就會將這些信息也包含在二進制數據裏,而且這些信息一塊兒進行網絡傳輸,這就致使數據傳輸量特別大,而jdk自帶的序列化機制會致使這些附帶信息更大,因此有必要使用比jdk更好的序列化機制,讓數據量變小,而且序列化和反序列化的效率更高,上篇文章裏我推薦了一種序列化框架hession,固然用戶想使用什麼序列化機制這個我也讓用戶能夠本身配置,這也是外部配置文件的一個選項。

    前面文章裏我還講到了壓縮技術而且推薦了google公司使用snappy,這個壓縮技術也是爲了讓網絡傳輸的數據量變小,提高網絡的傳輸效率。

    對於服務提供者和服務調用者我會提供一個jar包,這些工程都要引入這個jar包,同時還須要一個配置文件來定義一些須要用戶定義的參數,例如咱們使用一個名字叫ycdy_config.properties配置文件,裏面的key值介紹以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Config_center_url=ip:port;這個就是配置遠程管理中心的ip地址和端口號;
 
Server_type=provider/consumer;配置是服務調用者仍是提供者,不配置默認是提供者;
 
Provider_post=9999,這是服務提供者的端口號,調用者能夠不配置,其實調用配置了也沒啥用,若是提供者不配置,會有默認值的;
 
Provider_session_timeout=9000;服務提供者的超時時間,若是實際調用超過了這個時間,那麼說明服務調用超時,適用於服務調用者,提供者無效
 
Tick_time=3000;心跳時間,這是遠程調用中心檢測服務端心跳的間隔時間,適用於服務提供者;
 
Again_time=3;當服務提供者不通的時候,心跳反覆檢測的次數,超過了次數就標記該服務不可用;Provider_session_timeout、Tick_time和Again_time三者之間是有必定關係,這個關係要實現這具體把控了;
 
Ip_include_pattern=172\\.17\\.138.*|192\\.168\\.0\\..*,這個適用於服務提供者,由於一臺服務器可能存在多個ip地址,當遠程調用服務組件接收到提供者的ip,用這個配置項來辨認那個ip可用,這裏採用正則表達式的方式;
 
Ip_exclude_pattern=用於服務提供者,須要忽略的ip;
 
Consumer_policy=random/rotate;適用於調用者,調用者向提供者請求的負載均衡策略,我熟悉的只有兩種一種是使用隨機數,一種使輪詢,因此這裏目前就這兩種選項;
 
Monitor_log=true/false;是否開啓監控日誌,適用於服務提供者,任何系統日誌時最重要的,不然無法查生產問題,其實這個配置項應該能夠充實點,可是我如今還沒想好,因此先給個提示,具體到了生產看如何實現吧。


     你們看到了無論是做爲服務提供者仍是服務調用者使用的配置文件是一致的,並且一個應用既能夠配置成服務的調用者也能夠配置成服務的提供者,很是的靈活。

     遠程調用服務還須要一個重要的技術就是通信技術,這裏的通信技術我推薦nettynetty是個很是好的選擇,講到通信是個複雜的課題,若是之後有空我再作詳細介紹,通信層的東西是封裝到服務提供者和服務調用者引入的jar包裏,可是通信的ip地址和端口號則是須要遠程調用管理組件推送過來的。

     那麼在應用裏遠程調用服務到底如何使用了?哈哈,這時候spring就要上場了,咱們看看服務調用者和服務提供者的spring配置,以下所示:

 

複製代碼
<!-- 服務提供者配置 --> <bean id="serverProvider" class="cn.com.sharpxiajun.RmifSpringProviderBean"> <property name="interfaceName" value="cn.com.ITest"></property><!-- 遠程調用的接口 --> <property name="target" ref="clsTest"></property><!-- clsTest實現ITest的實現類,clsTest這裏是一個bean的id值 --> </bean> <!-- 服務調用者配置 --> <bean id="clientConsumer" class="cn.com.sharpxiajun.RmifSpringConsumerBean"> <property name="interfaceName" value="cn.com.ITest"></property><!-- value就是Provider定義的target的接口實現類 --> <property name="serialType" value="hessian"></property><!--序列化方式 --> <property name="compressEnabled" value="true"></property><!-- 壓縮標記 --> </bean>
複製代碼

 

     咱們發現這個新配置和之前不一樣了,這個配置將更加適合生成的開發。

     咱們首先看看serverProvider的設計,這個bean對應的classcn.com.sharpxiajun.RmifSpringProviderBean,裏面有個參數是一個interfaceName即提供者對外的接口,這裏我會使用反射機制將接口注入到RmifSpringProviderBean,而target則是具體的實現對象了,這就是業務對象,注意interfaceName必定要是接口,由於調用者會根據接口進行轉化,若是是類的話,那麼通用性就不好了。

     clientConsumer的設計,這個bean所對應的classcn.com.sharpxiajun.RmifSpringConsumerBean,其中interfaceNamevalue值對應的就是遠程定義的接口,和提供者的interfaceName保持一致,當提供者的數據傳導調用者後,就會根據這個雙方約定好的接口反序列化成能夠操做的對象,serialType是選擇序列化機制,不寫的話就是調用jdk的序列化機制,這裏附帶提下啊,外部的序列化程序也是放到jar包裏的哦,還有一個選項是compressEnabled做用是是否啓用傳輸報文壓縮。

    當調用者調用提供者服務時候,jar包裏netty程序會根據推送的信息(主要是ip,端口)和spring配置的bean結合起來就能夠完成一次服務的調用。

   好了,上篇寫好了,本篇主要是講解遠程調用服務的架構設計,我自我感受這篇文章比上篇更接地氣,但願看了本文的童鞋,能對遠程調用框架設計的原理更加清晰。

   其實netty的使用學問也很大,也是遠程調用服務的核心之一,本文這塊講的比較少,之後有時間我儘可能補充上這塊知識。

   下篇文章我將詳細介紹遠程調用框架裏使用到的zookeeper技術。

相關文章
相關標籤/搜索