天天看點

我對SOA的認識以及心得

注:本文來源于我給公司内部發的郵件中,是以背景都是基于我們現在的應用,而且思路也很混亂,請大家見諒。

自05年開始接觸到分布式架構,06年在原先的基礎上從頭開始設計了一套分布式架構,當時SOA這個概念也沒這麼火。整個大平台的開發、性能和可擴充性都得到了考驗,覺得有一些東西想和大家一起分享。

我不知道我所說的這些算不算真正的SOA,我也沒讀過什麼SOA的書籍,我覺得SOA這個概念非常抽象,任何概念的産生都是由原因的。是以,我也不會說一些抽象的原則,隻是想說一些在過去幾年實施“SOA”過程中的一些心得和一些細節,希望對大家有用。

       不說什麼是SOA,先來說說我們現有架構遇到的一些問題:

l   同樣一個邏輯,A系統(ASP)使用COM實作一份(我們現在的JAVA庫),B系統(.NET)在開發的時候覺得調用JAVA也是很麻煩的,索性自己實作一份邏輯,可能是存儲過程,也可能是在代碼中寫死SQL,C系統(由系統部門開發的.NET程式)也用到了相同的邏輯,由于和産品部門缺乏必要的溝通,也實作了一份邏輯。最後,如果這個相同的邏輯需要修改,則需要修改A、B、C三個系統,一份邏輯在三個地方實施有幾個缺點,一來是因為如果需要修改要改三個地方,二來是增加了工作量,三來是占用了不必要的資源(因為大家可能都實作了自己的緩存)。

l  所有網站全部部署在一個WEB伺服器上,直接實作負載均衡。這樣的方案有幾個缺點,一是幾乎沒一個網站程式都有自己的緩存,每台伺服器的緩存是備援的,而且甚至每個網站中的緩存都有重複,二是如果一個網站有問題會影響到其它網站(比如程序回收,應用程式池不能100%解決這個問題,而且我們現在并沒有注重應用程式池的劃分,又比如某些應用是長請求、過長時間占用線程),三是不能有效利用伺服器資源,這是因為不是每一個網站都具有相同的通路量,不是每一個網站都需要相同的資源(有些需要特别多IO、有些需要特别多CPU、有些甚至是記憶體)。

雖然說我們現在是使用了三層架構,但并沒有什麼重用,而且所有的層還是部署在相同的伺服器上的。為了解決前面的2大問題,我們首先想到了:

l  是不是有什麼方法可以讓相同的邏輯被其它系統重用?

l  是不是考慮把邏輯以服務的形式對外部(其他子產品)公開?

由此引入面向服務架構的概念,我們通過這些公開的服務進行邏輯的重用,提高系統性能也降低了子產品之間的耦合性。架構圖見我以前寫的文章http://www.cnblogs.com/lovecherry/archive/2008/06/18/1224496.html

如果确實采用這種架構,我們的開發方式會有什麼改變呢?

l  在一般情況下,我們一般認為A系統對應A資料庫,B系統對應B資料庫,每一個系統都有自己的資料庫。傳統的方式是A系統和B系統在資料庫端直接使用JOIN進行交叉耦合。如果實施SOA的話,最佳實踐應該是對于大多數系統來說,禁止A系統直接通路B系統的資料庫,反之亦然。我要你的資料,就必須調用你公開的服務。而且這個服務或者說接口或者說契約,盡量是粗粒度的。比如一個邏輯包括X和Y和Z三 個過程,如果獨立提供三個方法的話,每一次方法調用都是網絡調用的過程,性能比較低,而且更重要的原因,這個子產品提供了這麼細粒度的三個方法,如果這三個 構成了一定邏輯的話,很有可能這個邏輯就在調用方和提供方兩個子產品都實作了相同的邏輯。雖然說确實是提供了服務,但是沒有達到封裝邏輯這個重要的目的,而 且也産生了性能的下降,這種服務就顯得很不值得。

l  一 個大系統有許多子產品或者說子系統,每一個都有自己的複雜邏輯和存儲結構。開發人員可能隻熟悉自己的那一部分,如果我的系統确實要用到其它系統的資料,按道 理是應該想到調用它提供的程式集。很多時候我們并沒有這麼做,是因為我們并不知道對方有沒有提供我需要的功能,即使知道提供了也不知道怎麼去使用,如果要 去用的話可能熟悉對方系統的時間會比直接在資料庫中進行JOIN需要的時間還要長。這是一種惡性循環,因為這樣又導緻了一個邏輯在多處出現,一份資料在多處取得、儲存和緩存。有了SOA,我們應該能在SHAREPOINT或WIKI上看到一份清單,在哪個HTTP或TCP端 點上具有什麼服務,服務中有哪些方法,這些方法是幹什麼的,傳回什麼,傳入什麼,使用的注意事項,如果我開發的系統需要用到外部的資料,我第一時間想到的 不應該是去看資料庫中我需要的資料在哪裡,而是應該是去看看是否對方系統提供了這樣的服務,如果沒有提供的話則和服務的提供者進行一些溝通。你可能會問, 我作為系統的開發者,我也不知道要對外提供哪些服務,我也不知道别人需要用到什麼,确實是,但是至少我們現在可以做的是把内部使用到的一些邏輯以服務形式 對外公開,一般來說自己系統的這些函數如果能滿足自己要求的話從功能上來說可以滿足别人要求,隻不過别人可能需要的并不是這麼多罷了。

l  我們現在做TECH SPEC的時候可能關注内部的實作,如果是SOA的話,我們就需要關注現在在做的這個系統會用到别人的哪些接口,别人可能會用到我什麼接口,我需要公開出來。可能還會考慮,我用了ABC三 個系統的接口,是否需要把這個邏輯以粗粒度服務公開出來。每一個公開接口的參數、傳回都需要仔細考慮,把這些都列入到架構設計中,和架構師一起完成服務的 定義。開發人員可能隻對自己系統的接口和邏輯比較熟悉,架構師的作用是給開發人員建議,哪些接口你可能需要,哪些接口你可能需要對外提供,是否需要做緩 存。

l  我們現在的部署是非常簡單的,如果實施了SOA, 很有可能一個系統需要調用十幾個外部子系統的接口,每一個接口都需要制定位址、調用政策以及契約。位址和調用政策需要是可配置的,一般定義在配置檔案中或 者資料庫中,這樣一個系統的部署可能非常複雜,打個比方就像晶片的引腳一樣,有很多,一個引腳沒有接到合适的地方,系統就不能工作。雖然部署負責了,但是 系統之間的耦合非常小的,大家隻是依賴于某個網絡環境中的位址,依賴于某個契約。這樣的話,系統的伸縮性就很強,有些服務需要很高的資源,就給獨立的服務 器,有些服務占用資源很小,可以合并在一起。當然,也可以根據服務的性質,比如特别需要IO、特别需要CPU來配置設定到合适的伺服器上。伺服器并不一定是一樣的,有的伺服器記憶體特别大,有的伺服器CPU特别好。

l  還 會遇到一個問題,就是開發人員不太願意調用其它的接口。一是對别人的東西往往預設會覺得是實作糟糕的,性能很差的,二是覺得不放心,會不會到時候你沒給我 正确的資料,影響我的開發,産生互相推卸責任的問題。其實,這種想法不對,一個人不可能開發系統的全部,作為使用者來說要使用别人的接口信任别人的接口, 作為接口提供者來說需要積極對自己的接口負責,進行完善的單元測試,如果調用者有特别的需求在讨論後進行改進。這也就是說引入SOA的話,我們需要更多的溝通。

說的有點亂,接下來想說說我在實施過程中遇到的一些細節問題,很多時候SOA實施的失敗都是因為一些細節。

l  SOA的 接口設計必須基于業務的。架構師應該是一個總導演,對所有系統的業務都有一個認識,了解業務之間的關系,和開發人員一起定義合理的接口。這包括,接口是否 代表了業務、是否是合适的粒度、是否會有性能問題,别小看接口設計,一旦确定以後很難修改,接口的好壞決定成敗,我列為第一要素。

l  在實施中,管理很關鍵,有許多要點是需要有強制的。比如除非特殊需要不能直接引用其它資料庫,即使是本系統也應該引用本系統的服務,也就是說網站項目裡面沒有連接配接字元串、沒有資料通路邏輯,隻有端點的配置。還比如是A系統的開發人員有這個責任為A系統對外的所有接口進行後續的維護和功能擴充,不是說A系統結束了,我的資料我也不管,不行,别人如果需要的資料确實是我的資料的話,我就要管。

l  從性能角度考慮,一般内網中的服務通訊采用TCP(二進制序列化),公網的走HTTP。還有一種方式是IPC方式,程序間通訊,我們以前也用的挺多的,雖然說緩存應該在一個地方建立。但很多時候,某個方法的調用是每次通路頁面都需要進行一次或多次的,如果再進行網絡調用的話性能很成問題,比如論壇上的髒話過濾,如果某個髒話服務提供了髒話過濾的接口,如果這個這個接口需要TCP調用的話性能不高,這個時候我們會考慮把這個服務部署在WEB伺服器上,而不是APP伺服器上,IPC方式進行程序間通訊。

l  到最後我們會發現我們有20個子系統,也就至少有20個服務(一般是以Windows服務部署在APP伺服器上)。而且如果服務部署多份做負載均衡的話,可能就有上百個網絡位址。一個網站如果引用了5個服務,就需要一個一個IP地 址(或者說端點)進行配置,如果将來服務遷移,那麼這些網站的配置檔案修改是一個大問題。雖然說我們會有一個拓撲圖來描述網站之間服務的依賴情況,但是配 置檔案的修改工作量不小而且容易出錯。推薦的做法是有一個資料庫來存放所有服務的端點定義、描述,使用一個單獨的服務來提供所有服務的端點資訊,在網站中 隻需要配置這個服務的端點資訊,然後引用各種服務的契約就可以了。考慮到效率關系,在每一個WEB伺服器上都安裝有這個配置服務,以IPC方式提供所有網站進行調用,當然,其中的端點資訊都會做緩存。

l  前面說的是網站引用服務的端點資訊配置問題,還有一個問題就是服務的健康監視問題,服務是以Windows服務形式運作的,我們需要檢測Windows服務的狀态,占用的CPU和記憶體資訊。我們在每一個APP伺服器上又裝有一個AGENT服務,專門用于監視這些服務程序的情況,一旦發現問題會第一時間通知網絡部門甚至是開發人員,當然服務無響應的情況比較少,一般常見的情況是服務沒有正确部署(缺少DLL不能啟動),或者是占用了過多的記憶體。這個AGENT的另外一個作用就是收集伺服器上的錯誤日志,并且負責更新和重新開機服務。也就是說所有服務的更新是自動化的更新,我們釋出的時候釋出到指定的RELEASE伺服器,由專門的管理工具進行服務的自動更新,服務如果做負載均衡的話手動更新很麻煩。而且很多情況下相同的服務需要在多個伺服器部署,比如IPC服務 。

l  服務的執行個體管理問題,一般有SINGLETON和SINGLECALL等方式。推薦采用SINGLECALL方式,如果服務具有狀态的話會大大增加服務的開發難度,一是需要考慮線程同步問題,二是需要考慮生命周期。其實,我一直覺得有狀态的服務配合ORM資料通路層的使用是很好的,但做起來可能會遇到很多問題。

l  在存儲過程中做事務還是很簡單的,SOA如 果要做分布式事務的話,實作不是打大問題,但性能是很大的問題,很容易引起資料庫對象的死鎖。大多數情況下我們對分布式事務的替代方式是采用隊列,放到隊 列中的東西就認為是一定可以成功的,對于不使用隊列的情況,如果調用失敗了則記錄日志,不會進行復原。除非涉及到錢的地方才做分布式事務。

l  版本問題也是需要考慮的。傳統的實作方式是,如果新增接口的話以前的程式是沒有影響的,如果萬不得已需要修改接口的話就大吼一聲,我這個接口要改了,然後給大家發一份,請調用者按照我的要求修改一下,連同我的服務一起重新釋出。對于WCF的話,有MEX中繼資料交換,這樣就不用手動引用新的契約,隻需要從HTTP地 址上更新新的服務即可。但有一個不得不考慮的問題就是萬一在調用其它服務的時候發生錯誤了,我們怎麼知道是哪個服務的版本出現問題了呢。我們是這樣做的, 調用服務的代理有一層封裝,如果網絡調用發生錯誤的話,會記錄詳細的錯誤資訊,哪個服務(服務的版本)、哪個方法在哪個端點上的調用、傳入的參數是什麼, 傳回的是什麼。這樣,在部署的過程中發生錯誤或是版本問題,我們就很容易知道錯誤的原因了。那麼怎麼知道服務的版本呢?沒一個服務都約定有一個GetVersion方法,這個方法兩個作用,一是給管理工具測試這個服務是不是還有效,二是給配置服務擷取服務的版本号。

l  如果引入SOA的話,不可避免的是大大增加了整個系統的部署複雜性。想一下,在LIVE上,我們可能有20個WEB伺服器,10個APP伺服器(我們以前是2:1這樣進行配置的,服務按照類型和資源不同放在不同的APP伺服器,WEB伺服器CPU特别強,APP伺服器是廉價伺服器,有足夠大的記憶體,對于WEB程式來說确實沒什麼大的CPU計算,服務也就是緩存比較厲害)。10個APP伺服器上放100個服務,就産生了100個端點位址。開發人員其實不用考慮服務在LIVE上 怎麼部署的,需要架構師在部署的時候協調部署團隊一起完成部署工作,架構師知道哪個服務部署在哪個伺服器最合理,以哪種信道進行部署最合适。部署團隊需要 對各個伺服器的結構很清楚,也要學會使用健康監控工具(我前面提到的監控服務運作狀态的工具和異常管理工具來發現部署上的問題),這就對部署團隊的要求更 高了。實在不行的話可以要求開發人員參與,開發人員應該沒權限直接進行服務的部署,但是有權使用兩個工具的,一來用于排查問題,二來也會有很大的成就感。

l  服務監控工具以一個拓撲圖形式展現整個網絡上WEB伺服器、APP伺服器的IP位址、CPU記憶體使用,以及服務的引用情況和每一個服務對CPU和記憶體的占用,以及服務的健康情況。監控人員24小時值班負責監控,出問題的時候及時重新啟動相關服務。異常管理工具每天收集各個系統的異常,開發人員每天上班和下班的時候檢視一次這個工具,知道自己負責的子產品産生了哪個異常(異常有幾種,一種是程式有BUG,一種是外部有人在攻擊造成的,還有一種是前面說的服務的版本和運作問題産生的異常)。當然,架構師也應該時刻關注這2個工具,知道整個系統的健康情況。

l  從網絡上來說,APP伺服器應該跨2個網段,DB伺服器應該外部不能直接通路,所有資料庫操作都是通過APP伺服器提供的服務通路DB的,網站不具有任何連接配接字元串不能直接通路DB伺服器(不在同一網段)。WEB伺服器和APP伺服器之間應該走TCP,如果是跨國的話應該有VPN鍊路。

l  從軟體架構上來說,我們以前使用的是.NET Remoting對内,Web服務對外的方式,中間層以Windows服務作為載體。對于.NET 3.5時代,可以采用WCF+Windows服務承載的方式。WCF支援中繼資料交換是一個優點,另一個優點是友善TCP/HTTP各種綁定的切換,當然也可以同時提供多份綁定。如果是細粒度服務的話,可以使用ADO.NET DATA SERVICE+ADO.NET EF的方式直接進行提供。表現層可以直接使用ADO.NET DATA SERVICE也可以使用自定義的JSON/XML資料,不屬于SOA的範疇就不擴充了。

總結一下:

l  對于ETOWN的應用,SOA架構是絕對适合的。

l  實施SOA,技術不是問題,最大的問題還是管理和溝通(協作)。

l  實施SOA的話,我們需要重新建構一套基礎架構,提供統一的端點配置管理、異常管理、健康監控、契約協作平台等。不然到最後的配置和管理将會很混亂。

l  我們不需要更多的伺服器,但我們需要更合理的配置設定伺服器。而且整個系統的部署架構可能随着時間的遷移不斷調整的,合并壓力小的服務,拆散壓力大的服務。怎麼做負載均衡,這都是一直調整的。

l  實施SOA需要開發人員有更好的意識,包括協作意識,包括架構意識。當然,也需要架構師能夠參與到每個項目中去,心中對業務有大局觀。

l  配置可能是複雜的,需要有工具來確定這個複雜的過程。服務和網站怎麼配置是很重要的事情,需要架構師和開發人員讨論決定,不是說随便放上去能用就可以。

l  在做架構的時候要站在較高的角度來看,眼光要長遠,暫時的性能問題不一定是問題。按照網絡/磁盤/記憶體/CPU的層次來考慮。很多時候SOA和性能是有沖突的,就像不能一味覺得ORM性能不好就不去使用。考慮80/20原則,任何東西滿足了80的應用就達到了目的,剩下的20進行特殊處理。

補充幾點:

l  從服務契約設計上來說,應該讓這個契約盡可能獨立,不依賴于其它的契約。服務的調用過程不能依賴于服務本身的狀态,應該在任何情況下服務的作用不會由環境所變。

l  雖然說是内部實施,但最好資料契約的類型是标準類型,通用類型,原生類型,友善以後做開放API的時候真正實作對外的服務。其實SOA強調松耦合性,因異構平台協作的需求産生,強調一切基于消息,無關服務平台。

l  SOA的實作包括業務分析、契約規劃、服務管理、消費者管理、文檔管理、配置管理、疊代整合等重要步驟,不是說我要建立A契約就建立,我要消費A契約就消費,要展現在管理中。我一直覺得SOA不是元件更新到服務這麼簡單,軟體開發流程、管理流程、設計分析方法、配置管理都會有巨大的改變,前期準備要充分。

l  在建立前期需要制作完成一個基礎的架構供開發人員使用。比如說是不是需要擴充VS内建的WEB引用,使得生成的用戶端代理包括異常處理、端點配置(從配置服務中擷取)等内容。非業務相關異常的收集和發送等工作應該是自動的,無需編碼。由于調用的複雜性,服務可能還會調用服務,在部署的時候配置問題很難察覺,是以其中的異常處理非常重要。

l  項目完成後的代碼審閱需要對項目中用到的服務進行逐一檢查,如果有改變需要在拓撲圖(推薦有專門管理軟體來維護這個拓撲圖,并且還能檢查各個節點的健康狀态)中進行修改。必要的話還可以為每一個節點實作信任機制,得不到信任的消費者将不能消費。甚至這個工具可以和SHAREPOINT結合,也提供文檔的管理。

l  最理想的方式是實作服務的自動部署,全部依靠管理工具來完成,因為在伺服器上安裝Windows服務,需要複制檔案、解除安裝原來的服務、安裝新的服務、服務運作賬戶的配置,如果服務達到上百個的話,部署的壓力也比較大。

l  最後,什麼是SOA?個人覺得SOA是提供了系統級的松散組合和重用的基于消息的整合方案。

推薦閱讀: 《可伸縮性最佳實踐:來自eBay的經驗》

看圖說話:

我對SOA的認識以及心得

1、web伺服器做負載均衡。即使是做負載均衡也要按功能分割,隻有分割了才能有伸縮性。

2、app伺服器說實話壓力應該不會很大,主要是記憶體占的多一點。是以一般情況下沒有必要做負載均衡,而且一般情況下一個實體

伺服器上安裝多個服務。比如APP1

如果性能不行的話可以一個實體伺服器安裝一個服務,比如APP2。如果要做負載均衡的話可以随機選擇端點、可以按照主鍵範圍選

擇服務等方案。

3、主DB采用SAN和磁盤陣列。其它還有一些類型的伺服器,比如外部資源(CDN、google搜尋)、檔案伺服器(不是所有的資源都

需要傳到CDN,CDN延時比較大),一般的圖檔轉到專門的檔案伺服器即可,開放FTP供上傳,對外開放HTTP。還有比如郵件伺服器

等。

這和SOA沒什麼關系,隻不過提一下實體上的架構。

我對SOA的認識以及心得

好,現在拿一個網站做例子,比如認證網站,提供了使用者的注冊,使用者資訊向其它區域伺服器(比如遊戲伺服器)激活等功能。上

圖想展現的幾點是:

1、前面說的,對性能要求特别高的服務,可以以IPC方式程序間通訊。服務當然也就要安裝在本地了,無論是WEB伺服器還是APP服

務器。

比如說使用者在注冊的時候需要對某些關鍵詞進行過濾,這個關鍵詞的資料儲存在keyword DB,由keyword Service提供關鍵詞的過

濾服務,雖然說在這個服務裡面會緩存所有關鍵詞的清單,但是如果需要TCP調用服務的話,跨越網絡的性能還是很差的。是以哪

裡主機需要,服務就安裝在哪個主機上。

2、服務有兩種類型,一種是業務服務,比如Passport Service,其餘綠色的全部屬于基礎服務。比如說每一個機器都會有

endpoint service,提供了所有服務的端點資訊,我們根據key去定位到服務,如果服務位址需要改,隻需要在endpoint db中改變

端點資訊,然後重新啟動所有機器上的endpoint service即可(為了重新整理緩存)。說到緩存重新整理方式,一般從來不變的資料可以采

取被動重新整理,變化慢的資料可以采取定時主動重新整理。那麼,重新開機每一個機器上的基礎服務,比如前面說的endpoint服務也很麻煩,

怎麼辦?每一個安裝服務的機器還提供了service management agent服務,這個服務前面說過,一是用于管理機器上的各種服務,

二是用于監控它們,三是這些資料可以彙總到管理C/S程式上(第一個藍色方塊),提供統一的服務管理。還有一種必要的基礎服

務就是exception service,提供了統一的記錄異常的功能,當然也會有一個異常管理的B/S系統來管理這些異常。

3、每一個服務中都有一個粉紅色小方塊代表緩存。我想是的說緩存可以在各個層次去做,不一定隻有app伺服器可以做緩存。sql

server 你給它多點記憶體就是緩存多點,app上可以做資料的緩存,web伺服器上可以做頁面的緩存。你可能奇怪,記錄異常的服務

不就是把異常插入到資料庫嗎,緩存什麼呢。其實很多異常是重複的,比如有人在攻擊你産生的異常,我們在記憶體中維護不同類型

的異常,相同類型的異常在短時間内比如1小時,不會送出到資料庫去記錄。

4、再次要提一下一個原則,一個資料庫上的服務就像一個罩子,罩在了DB上,其它服務不能穿透這個罩子直接通路下面的資料庫

,要通路它的資料隻能通過這個服務提供的接口。比如這裡的passport service,一來是給passport website去用的,而其它很多

網站也需要會員資訊的資料啊?怎麼辦,就從passport service去拿,不能直接到資料庫中去拿。

5、圖中棕色的部門是一個異步隊列。這裡展現了《可伸縮性最佳實踐:來自eBay的經驗

》中的第四點那就是用異步政策去解耦程式。按照前面一個原則,假設有這麼一個場景。使用者在中心資料庫的資料需要插入到各個

區域的資料庫(比如使用者激活遊戲的過程就是把使用者注冊時的資訊,插入到遊戲的資料庫中,遊戲的資料庫在全國各地的)。要訪

問遊戲資料庫,我們肯定是通過專有的game service去做的,不會去直接連接配接遊戲資料庫。問題就來了,如果local game db不可

用,game service也不用,而passport service會調用game service去激活使用者,那麼

passport service這個功能也不可用嗎?這2個服務之間産生了很大的依賴,如果在資料庫中有一個隊列,passport service隻需

要把任務資訊送出到這個隊清單中,這樣就可以認為完成了激活任務。那麼passport service和game service之間沒有直接的依賴

。而game service作為一個隊列服務,隻需要定時從對清單中擷取需要激活的資訊,然後真正操作game db去做這個激活就可以了

。完成後再去隊清單更新狀态就可以了。通過一個異步過程來解除服務之間的耦合。