文章出處

一、下篇開頭的廢話

  終于開寫下篇了,這也是我寫遠程調用框架的第三篇文章,前兩篇都被博客園作為【編輯推薦】的文章,很興奮哦,嘿嘿~~~~,本人是個很臭美的人,一定得要截圖為證:

 

  今天是2014年的第一天,按中國辭舊迎新的傳統,也作為我2014年第一篇博客,我想開篇前要總結下2013年的技術學習。今年我當爸爸了,當爸爸的人是沒啥時間研究什么技術和寫博客的,所以2013年上半年我的文章很少很少,直到老婆5月回家待產才有重新拿起書,提起筆寫技術博客。今年老婆要回上海了,估計2014年的博客數量又會少點,但是不管什么情況,在技術上的不斷探索的態度我永遠都不會變,這是我生活的一部分,既是我的工作需要也是我的愛好所在,更是一種生存技能。

  2013年我的前端方面的文章比較少了,全年的文章主要是hadoop和系統架構,今年能抽出時間研究hadoop還得感謝公司給我的機會,雖然公司已經深切的感到hadoop所代表的大數據技術的重要性,但是我公司對hadoop的運用和領先的互聯網公司差距還很大,這個情況也引發了我的思考,也許大家認為新技術的運用往往需要領導的魄力,但我覺得一線員工對新技術的深刻理解以及合理的運用才是這個技術能否被公司應用的好的關鍵所在,這個關鍵還在于我們這些員工的推動該技術在公司的普及,我希望自己能成為有這種能力和意識的技術員工,一個能主動推進和運用技術的員工,而不只是用技術完成交辦開發任務的技術員工。

  2013年是我多年積累,厚積薄發的一年,我成功的在博客里分享了三種技術架構設計:一個前端開發框架,一個是分布式網站架構以及本文的主題遠程調用服務的技術架構,兩個都有成功的生產實際,一個是我自己的總結和思考,除了這三個,我還設計了一套session共享的技術架構,由于這個架構我向申請了發明專利,所以沒有拿出來和大家分享。又得感慨下了,如果是幾年前我很難相信自己可以做到這點,我堅信這是我多年堅持做好一件事情的成果,我現在也充分感受到這種堅持的成就感,這種爽歪歪的感受一定能堅定我在這條路上一直走下去,最終能成為一名在國內一流的程序員,這就是我職業的終極目標。

  2014年開始了,新的一年我要有新的進步,能給大家分享更多原創的博文,為更多新人答疑解惑,也希望自己能帶動更多人能投身與互聯網行業,在中國互聯網行業是非常好的行業,因為他是中國少有完全競爭的行業,中國實力可以比肩美國的頂級公司,更重要的是互聯網相對別的很多行業可以不拼爹不當二奶也能小有成就,實現自我價值。

  廢話結束,祝大家新的一年過的開心,充實,煩心的事情少之又少,開心的事情多之又多。

  遠程調用服務架構設計及zookeeper技術詳解--下篇現在開始。

 

二、遠程調用服務技術詳解---后續

 

  上篇結尾我說道希望本文是一篇接地氣的文章,既然要接地氣,那么最好有人看了本文后能自己實現出這套框架,如果要做到這點,我就得把架構設計的一些關鍵點和難點問題要點出來,告訴大家這些地方到底該如何實現,開發時候要注意哪些細節。本小節就是補充這些問題的。

 

  遠程調用管理組件和服務提供者之間有心跳機制檢測,它的作用是檢測服務提供者是否能正常對外提供服務,我在上篇提出的檢測方式是使用ping的方式檢測ip和端口號,其實具體實現中還可以有另外的方式,因為服務提供者都會引入遠程調用服務提供的jar包,在這個jar包里其實包含一個心跳測試接口,遠程調用管理組件采用服務提供者和服務調用者同樣的通訊方式去調用這個心跳測試接口,如果該接口能調通,那么心跳測試成功;如果發現一次調不通那么再反復測試幾次,要是還是不通,再標記該服務提供者的這臺服務器不可用,這種心跳檢測優于使用ping的方式,因為心跳檢測是完全模擬服務提供者和調用者的通訊場景,檢測的結果更加符合真實的情況。當心跳機制檢測出了服務提供者有些服務器出現了故障,我們應該還需要有一套機制及時通知運維人員或者相應的開發人員,做到故障的及時處理,所以遠程調用框架里應該還要包含一個監控系統健康性的監控模塊,如果你的公司有專門的監控系統那就更好了。

 

  遠程調用管理組件和服務調用者還有一個推送信息的關系,這里我再描述下這個推送的關系,當服務提供者啟動的時候會發送自己的ip地址和端口號,遠程調用管理組件接收到這些信息然后把這個ip和端口號推送到服務調用者,其實這個描述缺乏細節,經不起推敲,做個Web開發的童鞋都知道想訪問一個應用除了ip地址和端口號,還需要應用的名稱,我的配置文件里只推送ip和端口號,沒有應用標記確定是哪個應用,那么調用者如何確定自己到底是調用了那個應用服務了?對于服務提供者真的不需要應用名稱,只需要提供ip地址和端口號就行了,因為不管你是那種服務,對于調用者而言看到的都是遠程調用的服務名稱,所以遠程調用管理組件只需要向服務調用者推送ip地址和端口號,而服務名稱就是遠程調用服務的名稱,服務調用者到底調用哪個服務,這個就是上篇里看到的spring配置里的接口,這個接口就是具體服務的身份證了。

 

  推送里還有個細節我沒有講清楚,遠程調用管理組件怎么知道服務調用者需要調用那些服務呢?這是我上篇設計方案里的漏洞,因此這里我要將這個設計補充進去,服務調用者到底調用那些服務提供者,這個配置可以做到服務調用者的配置文件里,但是這么做就會讓服務調用者和服務提供者進行了一定程度的耦合,同時也架空了遠程調用管理組件的作用,所以把它做到服務調用者這邊不是太明智的,做到遠程調用管理組件里才是最佳最可行的方案。這里我要為遠程調用管理組件開發一套管理它的Web應用,并且在上篇的配置文件里加上一個標記服務類型的服務標記,該標記只適合于服務提供者,在服務提供者啟動的時候一起推送到遠程調用管理組件里,而Web應用一個重要的功能,就是根據服務調用者的ip地址和這些標記建立對照關系,那么遠程調用管理組件就可以知道服務調用者需要調用那些服務了。好了,服務調用者和服務提供者的關系建立好了,那遠程調用管理組件是如何推送服務信息到服務調用者的呢?我們發現遠程調用管理組件和服務調用者之間關系是一個典型的發布-訂閱的關系,發布-訂閱的模式在設計模式里有一套解決方案:觀察者模式,這里我先簡單介紹下觀察者模式:

 

  觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態發生變化的時候會通知所有觀察者對象,使得它們能夠自動更新自己。

 

  關于觀察者的詳細信息,大家可以查看下面的文章:

 

  http://www.blogjava.net/supercrsky/articles/202544.html

 

  通過觀察者模式的定義,我們發現遠程調用管理組件即是主題對象,服務調用者則是觀察者,其實程序里信息推送的模式都是使用觀察者模式來實現的。

 

  在上篇里我給大伙展示服務提供者和服務調用者的spring配置文件,接著告訴大家提供者和調用者就是使用RmifSpringProviderBeanRmifSpringConsumerBean這兩個bean就能完成相互的通訊,我開始以為自己已經講的很清楚了,有代碼有真相,但是結果是馬上有童鞋跟我私聊,問道這到底是怎么做到的呢?,事后我再想想,如果這個問題不解釋細點還真是有很多人不明白。這里我使用的是代理模式,下面是對代理模式的簡單介紹:

 

  代理模式及Proxy,是為其他對象提供一種代理控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介作用。

 

  遠程調用服務的一個核心設計就是要保證服務提供者和服務調用者之間的調用規則要一致,但是具體到業務的處理又能多種多樣,這個場景就是一個典型的代理模式。至于java里代理模式的詳細情況,可以參看下面的這篇文章。

 

      http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

 

  在遠程調用服務里的代理模式不是一個普通的代理模式,因為這種代理關系的建立都是在程序運行中進行的,所以我們必須選擇使用反射機制實現的動態代理模式,讀完上面我引用的文章,大家發現了吧,我在spring里使用的是interface方式,這說明我的方案是使用jdk自帶的代理模式,不過使用cglib的動態代理,用實現類來實現代理我感覺可能程序設計會更加靈活些。

 

  遠程調用服務的框架開發里,還有兩個重要的技術:線程和通訊,這兩個技術是遠程調用框架的核心技術也是難點技術,因為線程和通訊都比較復雜,所以本文不展開它們具體的實現方式,而是談談它們在遠程調用框架里的作用。

 

  在談作用之前我要插入一點內容,上篇里我講到遠程調用服務框架就是幾年前很火的SOA里的服務總線,如果有童鞋接觸過這種骨灰級的技術,一定知道當時的服務總線有個簡稱叫ESB總線,而ESB總線的通訊介質幾乎都是WebService,當年的SOA是源自企業級的解決方案,而我現在設計的遠程調用服務是針對于互聯網的應用,互聯網的遠程調用框架沒有采用WebService而是使用新的通訊介質例如netty,或者是apache的頂級項目mina,其實當下webservice也是一種被淘汰的技術,淘汰的原因是它的效率低下,不管是傳輸的數據大小,還是請求響應的效率都很爛,互聯網的遠程調用服務是一個高性能的框架,它的性能更好,而且它包含了原來ESB總線的所有特點,由此可見互聯網的技術遠遠領先企業應用,企業級的應用企業相對于互聯網企業就是傳統手工業,而當下互聯網的技術已經開始引領企業的應用,這就是落后就要挨打的道理,沒有創新的意識就會被別人革命。

 

  既然我們要設計的是高性能的遠程調用服務框架,那么高性能的一個指標就是高并發,而高并發就是指你的服務到底能開啟多少個線程,所以線程寫的好壞直接關系到遠程調用服務可用性的好與壞,使用線程的時候要注意以下幾個問題:第一,服務器的內核一般是多核的,所以編寫線程的時候要把這些內核都利用上去,大量的線程要使用那么一定要使用上池技術,這里我推薦的是jdk自帶的Executor框架,Executor框架設計的非常好,同時和netty的兼容性也非常不錯,最后就是線程安全的問題,這里最難的就是線程安全,如果讓我自己實現線程,我對寫線程安全的信心是最不足的。

 

  高性能的另一個指標就是網絡通訊了,這里我采用的是netty框架,Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。Netty有很多優點,但是它最大的優點是netty的長連接和事件驅動,netty能幫我們屏蔽很多socket難以控制的細節,它的長連接設計的非常不錯,減少了tcp開啟和關閉的開銷,而事件驅動模式又大大降低了開發的難度,但是netty本身有個缺陷,這個缺陷就是netty是一個異步的通訊框架,但是大家看到了我們調用的場景都是同步,因此我們必須把異步請求轉為同步請求,在前面的分布式網站架構里,我使用到了這種技術,哎,可惜當時使用時候是拿來主義,異步轉同步的詳情我現在還不是很明了,這個也是我以后需要研究研究的,這里先預留下來,以后明白了再寫博客告訴大家。

 

  好了,遠程調用服務的架構設計詳情到此結束了,下面是本文的另一個主題zookeeper了。

 

三、Zookeeper技術詳解

 

  在遠程調用服務里zookeeper使用在遠程調用管理組件里,而服務調用者則是zookeeper的客戶端,遠程調用管理組件也是遠程調用服務的核心所在,如果遠程調用管理組件在運行時掛掉了,那么整個應用也將不可用,因此遠程調用管理組件必須是否可靠,這種可靠性的要求甚至要高于服務提供者和服務調用者的可靠性的級別,因此遠程調用管理組件的設計必須是分布式的,而且一定要是可靠的分布式。

 

  遠程調用管理組件是一個完全符合zookeeper場景的應用或者說是一個標準的zookeeper應用,為了便于后面的論述,這里我的更進一步的講解下遠程調用管理組件的功能,從我前面的敘述,我們知道了遠程調用管理組件核心作用是存儲服務提供者和服務調用者通訊的配置信息,例如存儲服務提供者的ip地址和端口,記錄服務提供者的服務類別,它還記錄服務調用者的ip地址和端口號(這個信息是在遠程調用管理組件的Web管理系統里錄入的)以及服務調用者和服務提供者的對照關系。對于服務提供者,遠程調用管理組件還提供心跳機制檢測服務提供者的健康狀態,如果遠程調用管理組件發現服務提供者一些服務器出問題了,它會及時更新服務提供者的配置信息,并將這些變化及時推送給服務調用者。由上所述,從配置信息存儲的角度,遠程調用管理組件其實就是一個遠程存儲配置信息的系統,而心跳機制和推送機制這是一種觀察者模式,而上面這些功能都是在分布式環境下功能,需要很高的可靠性。本文上篇的開頭,我曾提到在hadoop技術生態圈里,zookeeper曾是最讓我困惑的技術,這種解困的好奇心是促使我一直很關注zookeeper技術的一個重要誘因,現在我有點理解zookeeper為何物了(不管說深入理解,因為每個技術都是博大精深,永無止盡的,特別是很優秀的技術)Zookeeper最典型的一個應用就是可以做分布式應用的配置服務,具體點就是像我們平時寫的配置文件,到了分布式系統里也是需要一個獨立的系統來完成,并且是動態的配置服務。

 

  既然zookeeper可以做分布式的配置服務,那么我們可以通過分布式配置服務的特點反向的理解zookeeper的作用。我是一名java工程師,在做javaweb開發的時候,會使用大量的配置文件,一般這些文件是用properties屬性文件完成的,服務啟動時候,屬性文件里的信息會讀到內存中,web系統從內存中讀取這些配置信息,這個配置信息有幾個特點:屬性文件一般不太大(這里指的是系統運行相關的配置文件,大伙不要把國際化的也拿過來理解啊),配置信息是持久化,使用時候是先加載在內存中的,從內存讀取,zookeeper也可以完成這樣的事情,而且其特點和傳統的配置文件使用幾乎一樣,zookeeper有一個文件系統,這個文件系統是用來存儲小文件的,我們讀取配置信息時候是在內存里讀的,效率很高,寫信息的時候zookeeper會將配置信息持久化。這就是為什么有的書里介紹zookeeper的性能:

 

  Zookeeper的基準吞吐量可以超過10000個操作,而對于讀操作為主的工作負載,吞吐量更是高出好幾倍。

 

  這句話很有道理,小文件寫速度很快,10000個操作木有啥問題,讀是通過內存,高好幾倍是理所當然的。

 

  系統運行的配置信息可靠性要求是很高的,既然我們現在使用分布式系統完成配置信息的讀寫操作,那么確保信息讀寫的準確性是非常重要,特別是寫,要求絕對是要么成功要么失敗,這個場景估計很多人一看就認為這不就是線程安全嗎?沒錯,是線程安全(能想到線程安全還是很牛的哦),但是大伙要看清楚啊,我們現在是分布式系統,不同服務器之間的操作不是線程的范疇而是進程的范疇,因此這里就需要新的保證操作安全的技術,換句話說就是進程安全的機制。除此之外,分布式的配置服務之所以使用分布式就是為了保證配置服務系統的穩定性和安全性,這樣才能持續為用戶提供高質量的服務,這兩個難題看似毫不相關,但是卻有一種方案同時解決這個問題,這個方案就是zookeeperZab協議,為了說清楚Zab協議,我們舉個例子,例如我們使用5臺服務器作為zookeeper服務器,我們向zookeeper集群發送指令,這個指令就是讀操作或者寫操作,zookeeper集群會完成下面兩個操作:

 

  操作一:領導者選舉,當zookeeper啟動的時候,這5臺服務器會選舉出一個領導者機器,其他的機器則是追隨者,如果有半數以上的追隨者和這個領導者通訊完畢確認了狀態,那么這個階段也就完成了。如果領導者一直都很健康,那么領導選舉的操作就不會再促發,如果領導者出問題了,那么zookeeper就會再一次促發領導者選舉的操作。(這里有個問題我不太確定,就是zookeeper的領導者檢測追隨者健康性,應該也會使用心跳機制吧?如果有哪位大俠知道,可以給我評論下哦)

 

  操作二:如果發出的指令是寫請求,那么所有的寫請求都會被轉發到領導者,再由領導者將更新的廣播發送給追隨者,當半數以上的追隨者將修改持久化后,領導者才會提交這個更新,然后客戶端才能收到一個更新成功的響應。這個用來達成共識的方式被設計成原子性的,這個操作要不成功要不就失敗。

 

  由以上操作就保證了讀寫的原子性,不會發生臟數據,反復選舉領導者也保證了服務的可靠性。當然這里還有個問題,要是領導者出現故障了?這時候zookeeper集群又會重復上面的領導者選舉操作。這也說明為什么zookeeper集群要求是奇數臺的服務器,5臺服務器2臺掛了,服務任然可以正常運行,如果是6太服務器,還是只能允許2臺服務器出故障,因為如果3臺掛了,剩下的服務器沒有過半數,那么zookeeper自己都掛了,所以奇數服務器不會造成服務器資源浪費。

 

  對于讀操作,zookeeper任意一臺服務器都可以直接給服務,附帶其他操作很少,所以高效;而寫操作,只有當所有服務器都持久化了數據后,zookeeper才會更新內存中對應的數據,所以會比讀操作慢多了。

 

  Zookeeper存儲數據操作方式和Unix文件系統的路徑操作一致,而內存數據存儲的模型就是一個樹狀結構了,樹狀結構的節點叫做znodeznode就是用來存儲和讀取數據的地方,這個樹的操作如下列表:

 

  我們存儲的配置信息就是使用這些操作完成的,例如:當服務提供者啟動時候將自己的配置信息推送到遠程調用管理組件,組件就會做創建節點或者設置znode所保存數據的操作,當數據保存成功后,zookeeper就會馬上將信息推送到服務調用者,這個推送工作zookeeper也可以完成,zookeeper里的znode以某種形式發生了變化,每個znode上面都附帶一個觀察機制,也就是我們前面說的觀察者模式的應用,這個觀察者機制會給客戶端通知,而這個客戶端就是服務調用者。如果心跳機制檢測到服務提供者某個服務器出故障了,zookeeper也會修改相應的znode的信息,這時候也會促發觀察機制,通知服務調用者發生了變化。

  好了,本文主題就寫完了。

  Zookeeper的功能很強大,不僅僅包含上面我談到的運用,本文是我深入學習zookeeper的開始,有了現在的基礎,再深入學習zookeeper將會容易很多。

  2014年第一篇博文書寫完畢,祝大家元旦快樂!

 

 

 

 


文章列表


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

    IT工程師數位筆記本

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