文章出處

一、序言

     Hadoop是一個技術生態圈,zookeeperhadoop生態圈里一個非常重要的技術,當我研究學習hadoop的相關技術時候,有兩塊知識曾經讓我十分的困惑,一個是hbase,一個就是zookeeperhbase的困惑源自于它在顛覆了我對數據庫建模的理解,而zookeeper的困惑卻是我無法理解它到底是干嘛的。

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

    http://www.cnblogs.com/sharpxiajun/p/3297852.html

   其實這篇文章寫完后,我自己感覺并不是太好,因為寫本文的時候,對遠程調用服務的設計以及zookeeper的理解都不是很到位,但是這篇文章還是受到了大家很大的關注,被博客園作為了推薦文章,還有很多網友希望我寫一篇更加詳盡的文章,還有童鞋留言說這個設計方案和淘寶開源的dubbohsf類似。我相信大家的關注就是意味著這個主題是當下技術的熱點,所以今天我要寫一篇主題和上篇文章一樣的博文,這是上篇文章的升級版,不管是遠程調用服務還是zookeeper我都會給出更加詳盡的講解。不過這里還是要說明下,這篇文章里遠程服務的設計我還是沒有參照dubbo,因為最近實在太忙,沒有時間研究淘寶的dubbo,但是我希望學習過dubbo的童鞋可以幫我對比下我的方案和dubbo的區別,區別就會產生新的問題,也會有新的知識需要研究。

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

 

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

    首先我們要再深入理解下為什么應用軟件服務里需要一個遠程調用服務,遠程調用服務解決了軟件設計中的什么問題,它的架構設計又有什么理論根據了?

    我曾寫了一篇關于分布式網站架構設計的文章,文章地址是:

    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多的業務服務端系統融為一個整體,網站的規模會越來越大,提供的服務也會越來越多,這既避免重復造輪子的問題還使得網站規模越來越大。

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

 

     有了遠程調用服務,我們可以做到業務級別的集群,例如:一個制造企業,一般都會有采購業務,生產業務、銷售業務以及財務業務,按照傳統的思路我們都會給每個業務獨立開發一個系統,如果引用了遠程調用服務,我們可以將這些業務都做成獨立的服務,這些服務組成業務集群,而這些服務都是用統一的遠程調用服務作為操作的入口,換句話說不管什么樣的服務對于調用者來說都是統一的,這樣前端的調用者可以做到應用的統一,所謂的應用的統一淘寶網站是最典型的代表,我們在一個同一的網站里可以操作各種不同的應用,而不會發生因為應用的不同我們就得重新訪問新的地址或者重新登錄到另外一個系統里做其他業務的操作。而服務端這邊,完全可以擺脫傳統的客戶端和服務端耦合的開發,增強了整個服務端的專業性和穩定性,這樣更易于服務端的擴展性和可維護性。如果服務端之間也需要相互調用也可以通過遠程調用服務實現,由于遠程調用服務的統一性,這樣就避免了服務調用之間報文和調用方式的不統一,規范了整個開發的流程。如果遠程調用服務還有負載均衡功能,整個服務集群就變成了一個私有的云,所以說遠程調用服務是云計算的重要組成部分,這個說法一點都不為過。

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

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

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

   這里談談SOA的目的是想起到拋磚引玉的作用,讓那些想深入研究遠程調用服務的人可以從SOA的角度理解遠程調用服務,而那些還是不明白遠程調用是何物的童鞋可以通過SOA的概念來理解遠程調用服務。

 

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

     遠程調用服務技術詳解,詳解,嗚嗚~~,這兩個字很有壓力,我怕有童鞋看了這個標題會以為我會將整套技術實現方案寫到里面,這個難度太高了,寫幾萬字估計都說不清楚,再說真的寫的那么細致,估計很多人都看不懂了(嘿嘿,我自己也沒有技術實現過哦,這些都是構思,構思哦),所以詳解就是詳解原理。

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

 

     傳統的服務調用都是服務提供者和服務調用者的直接調用,從架構圖里我們看到這里多了一個遠程調用管理組件,遠程調用管理組件是一個獨立的服務系統,為了保證該系統的穩定性,它也一定是一個分布式的系統,但是這個分布式系統和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值介紹如下:

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技術。

   這是2013年的最后一篇博文了,祝大家新年快樂哦。

 

 

 

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()