關于架構,筆者認為并不是越複雜越好,而是相反,簡單就是硬道理也提現在這裡。這也是微服務能夠流行的原因,看看市場上曾經出現的服務架構:EJB、SCA、Dubbo等等,都比微服務先進,都比微服務功能完善,但它們都沒有微服務這麼深入民心,就是因為他們過于複雜。簡單就是高科技,蘋果手機據說專門有個團隊研究如何能讓使用者更加簡單的操作。大公司都是由小公司發展起來的,如果小公司在開始技術選型時感覺某個架構費時費力就不會選擇,而小公司發展到大公司的過程,一般也伴随着系統不斷優化的過程,而不斷優化往往不會重新選擇開發技術和架構,而是在原來基礎改進,這也許就是簡單架構流行的本質。
假設我們需要為超高業務量的保險代理企業設計一個“網際網路+”保險平台。假設這家保險代理企業網上保險注冊使用者規模為2千萬,門店及×××商銷售人員2萬,年保單量2億單(中國平安總使用者規模達1.67億,擁有超過79.8萬名壽險銷售人員和約24.6萬名正式雇員。截至2015年6月30日,集團總資産達4.63萬億元,歸屬母公司股東權益為3,311.90億元。而目前網際網路保險領頭羊衆安保險,經營以小額貸款為主,由于背靠阿裡巴巴,日保單銷售量可達1億,不過别人很難複制衆安保險的模式)。是以我們取大型網際網路企業和衆安保險的折衷來考慮這個保險O2O平台。
l 需求分析
參考保險業務相關文檔(文檔不全),獲得如下核心需求矩陣(因為涉及功能太多,隻取大的功能點)。
分類 | 功能 | 品質 | 限制 |
電子商務(B2C) | 産品展示(搜尋、詳情展示等) | 及時響應、安全性、健壯性、易用性 | 多種險種,處理方式可能不同 |
産品購買(送出訂單、支付) | |||
使用者中心(我的保單、我的理賠等) | |||
代理人管理(×××商管理) | 車險投保(詢價、錄單、繳費) | 及時響應、健壯性、可擴充性 | |
非車險投保(詢價、錄單、繳費) | |||
保單查詢 | |||
單證管理 | |||
我的賬戶(我的保單、傭金結算等) | 及時響應、安全性、可靠性、易用性 | ||
案卷管理 | 案卷錄入 | ||
索賠資料收取 | |||
案卷交接 | |||
案卷跟蹤 | |||
客戶管理 | 客戶資訊維護 | 上傳大量檔案 | |
客戶活動管理 | |||
商機管理 | |||
我的工作台(消息、活動、商機) | |||
保險公估 | 車險定損過程跟蹤協助 | ||
人傷出險協助 | |||
法律援助服務 |
從O2O的概念來看:“O2O即Online To Offline,也即将線下商務的機會與網際網路結合在了一起,讓網際網路成為線下交易的前台。這樣線下服務就可以用線上來攬客,消費者可以用線上來篩選服務,還有成交可以線上結算,很快達到規模。該模式最重要的特點是:推廣效果可查,每筆交易可跟蹤(百度百科)”,不是每一種服務都适合O2O。商品類的銷售不适合O2O,因為你直接從網店就可以購買根本不需要線下店。但保險類服務的确需要線下和線上結合,如果是純線上将不能滿足客戶的服務需求。O2O的線下服務可以是×××商、代理人,也可以是直營店。
上面的需求是按照使用者角度提出的,雖然使用“系統”一詞,但這裡的系統是一個抽象概念,可能包括軟體系統以及人事、制度等在内。上面的需求可以分為三大類,一類是針對終端使用者純線上服務:電子商務網站(B2C);一類是專門針對代理人服務的:代理人系統;剩下的是一些公共服務,有可能電子商務網站和代理人系統都會用到的一些服務。另外保險業務最大的一個問題是多個險種,不同險種處理方式有很大不同,這跟普通電子商務網站差別很大,比如天貓,所有商品都是同樣的下單方式,同樣售後服務(主要就是快遞這塊),而保險産品即使是線上B2C網站下單操作,短險、壽險、車險等也是不同的,更何況保險有一大堆售後服務,這些售後服務更加不相同。傳統保險公司在處理這方面時,一般會做多個系統,比如壽險系統,車險系統等等。
l 系統分析
安裝之前提到的業務規模我們分析(假設)出一些比較重要的性能需求:
a)産品方面:繼續上線目前沒有的保險産品
b)B2C網站日通路量:5000萬PV
c)B2C産品購買并發高峰:2000 TPS
d)運維系統同時線上:1萬(共有2萬銷售員或代理人)
e)運維系統并發高峰:2000 TPS
f)短險訂單:每年1.85億單
g)長險訂單:每年500萬
h)車險訂單:每年1000萬
i)案卷資訊:每年新增100萬單
日通路量5000萬和産品并發2000
TPS是我們假設的,客戶資訊和案卷資訊是随訂單資料量變化而變化,在前面我們雖然假設了總共每年産生2億個訂單,但是根據保險種類,短險(旅遊險、傷殘險)明顯産生了90%的訂單量,這一點需要特殊處理。除此之外車險和長險(主要指壽險等)無論是投保還是售後服務都有明顯不同,是以也需要特殊處理。
那麼我們按照上面的需求,進行系統分析,首先按大的職責将職責相同的劃分為一個服務。并且有了上面這個性能需求,所有功能需求都需要增加一項“品質”特性,那就是“高并發”,高并發會影響到所有設計。另外如果要将網際網路保險平台品質特性排個序,最重要的是可擴充性、安全性,因為保險的種類多而且處理方式不同,除此之外,高并發和可靠性也會直接影響功能的實作,但并沒有可擴充性影響大。深入分析職責後把每一種功能的實作關鍵技術列出,如下:
需求分類 | 實作需求 | 實作子系統及服務 | 軟硬體實作技術 |
用戶端 | B2C電子商務網站 | B2C Web用戶端 | 叢集部署、高速緩存、分布式緩存、搜尋引擎技術、靜态化 |
B2C電子商務網站手機用戶端 | B2C App用戶端 | ||
代理人管理 | 代理人Web用戶端 | ||
代理人管理手機用戶端 | 代理人App用戶端 | ||
案卷處理管理 | 案卷處理Web用戶端 | 叢集部署、分布式緩存 | |
客戶管理管理 | 客戶管理Web用戶端 | ||
保險公估管理 | 保險公估Web用戶端 | ||
運維産品管理 | 産品管理Web用戶端 | ||
報表及财務統計 | 報表及财務統計Web用戶端 | ||
公共服務 | 運維産品管理、Web前端産品通路 | 産品服務 | |
電子商務或代理人訂單管理 | 訂單服務 | ||
電子商務或代理人等涉及财務操作 | 總賬服務 | ||
報表服務 | |||
業務服務 | B2C電子商務網站及手機用戶端個人賬戶 | B2C個人賬戶服務 | |
代理人管理服務 | |||
案卷處理管理服務 | |||
客戶管理服務 | |||
保險公估管理服務 | |||
短險開放式接入 | 開放式接入平台服務 | ||
工具性服務 | 保險公司産品對接 | 産品對接服務 | 叢集部署、消息隊列 |
線上支付 | 第三方支付服務 | 叢集部署 | |
短信郵件通知 | 通知服務 | ||
性能監控 | 日志采集服務 | ||
檔案伺服器 | 檔案服務 | ||
服務授權與審計 | 服務授權與審計服務 | ||
分布式事務管理 | 分布式事務管理服務 | ||
定時任務管理 | 定時任務服務 |
各個子系統及子產品的關系如下圖。
其中訂單服務、産品服務、财務服務、工具服務為基礎服務,其它各個業務子產品的服務會調用這些基礎服務。各個業務子產品的服務都是根據業務領域進行劃分的,同一業務領域下實作技術不同會被劃分為兩個服務,比如産品展示和訂單原本屬于同一個大的領域,但其因為實作技術和品質要求不同需要劃分為兩個服務。因為短險接入量大,而且大部分是跟第三方合作接入,是以設計短險接入公共接口服務平台處理大量短險訂單。
l 存儲及緩存架構
對于大型的高并發系統來講,最重要的當屬資料的架構。我們在前面也提到過,web系統業務處理子產品本身就可以叢集部署,當使用者出現高并發時最先遇到的瓶頸就是資料庫通路的瓶頸。這也是我們說資料架構最為重要的原因。其實web系統是典型的“計算機資訊系統”,也就是說一切以資料(資訊)為基礎,所有的功能都是圍繞着資料來的。這也是我們在這裡說資料是web系統最重要的,很多公司在做少使用者量web系統時直接設計好資料庫就可以開發了。
按照上面劃分的業務領域我們設計多個資料庫,技術選項包括“是否讀寫分離”、“是否水準切分”及“路由鍵”。其中路由鍵是指在進行水準切分後,我們使用那個“辨別”去查詢資料庫,一般來說會使用該業務領域聚合根對象的主鍵作為路由鍵。
資料庫 | 是否讀寫分離 | 是否水準切分 | 水準切分路由鍵 |
産品資料庫 | 是 | ||
訂單資料庫 | 客戶ID | ||
公共資料庫(中繼資料、公共資料) | |||
客戶管理資料庫 | |||
案卷管理資料庫 | |||
代理人服務資料庫 | 代理人ID | ||
B2C電子商務個人賬戶資料庫 | |||
保險公估資料庫 | |||
工具資料庫(短信、支付、檔案) | |||
報表資料庫 | |||
日志采集服務資料庫 | 日志ID |
除工具資料庫外,其它的資料庫的劃分很容易了解。工具資料庫的資料也大都跟客戶或說使用者有關,比如“産品對接服務”,産品對接服務是指使用者在購買了保單後,系統會自動對接到具體的保險公司接口去上傳保單資訊和下載下傳保單,是以水準切分資料庫時可以采用使用者ID作為路由鍵。“線上支付”和“通知服務”也是類似,都是儲存使用者相關的資料,線上支付服務儲存的是使用者線上支付的流水,通知服務儲存的是發送給某使用者的短信或郵件。
另外在資料架構中我們也可以看到一個規律,就是使用了水準分庫的存儲結構就不能再使用讀寫分離,原因是防止存儲單元過度泛濫,因為使用了水準分庫之後,本身也要為資料庫建立多個備份庫,這個時候如果再用讀寫分離需要建立一套隻讀庫,資料庫的數量将增加一倍。使用了分庫後我們可以把熱點資料存儲在分布式緩存中以起到讀寫分離類似的作用。
另外緩存也是存儲技術的一種,而且非常重要。從日常生活中我們也可以看到這一點。比如你要去購買一台電腦,你會發現二級緩存和記憶體大的價格高出很多,如果你的顯示卡顯存巨大,那将是頂級配置,發燒級配置。手機也是一樣,記憶體大的手機速度明顯快,價格也高,如果記憶體和持久化存儲都高,那也是頂級配置。對于web系統也是一樣,有些大型web系統隻要緩存處理的好,資料庫不需要分庫就可以承載億級的使用者,比如維基百科、新浪微網誌等。也是以,不管是電子商務網站還是網際網路+大型應用,都會在它們的架構中看到緩存大量使用的情況。
從整個web系統的架構來看,緩存在兩個層面大量使用,分别是展示層和邏輯層,展示層通常使用高速的頁面緩存,邏輯層通常使用高并發的分布式緩存。當然有些分布式緩存工具既可以在邏輯層使用也可以在顯示層使用。
系統 | 是否使用CDN | 是否使用高速緩存伺服器(varnish) | 是否使用分布式緩存(redis) |
報表及财務統計Web端 | |||
短險公共平台服務 |
l 邏輯架構
在給出系統總體的邏輯架構前,我們先看看系統前端和服務層直接的關系。前端是用戶端,可以有多個用戶端,也可以有多種用戶端,比如手機端(APP、WAP、微信)等。用戶端和服務之間的架構是典型的MVC架構,V是用戶端,C就是spring
mvc或servlet開發的控制層,對于Web應用V和C在開發角度是一個工程,剩下的M就是服務層。這是對于web系統來說的,對于app或者使用html直接實作的用戶端,或者是.net實作的桌面用戶端,由于這些用戶端是單獨開發的,沒有控制層,是以需要增加一層“API
網關”作為這些用戶端的控制層。
圖中的服務元件就是指各個業務服務,這些服務可以看成是元件的概念。通常前端界面一個界面中需要的資料通常不僅僅來自于同一個服務,即使是來自于同一個服務,那也來自于很多不同的接口,servlet或api
網關作為控制層,所起到的作用就是組合接口、組合資料,并處理接口間的事務性。另外服務元件也是分層的,圖中并沒有展現,一般可以分為3層,從低到高依次是工具性服務元件、基礎業務層服務元件、業務層服務元件。前端界面的請求按照從高到底向下傳遞和處理請求。
按照職責、通用性、技術特性綜合考慮和計量,邏輯架構設計如下圖:
十幾個子系統分别分布在服務層、控制層、表現層(典型的三層架構)。實體層和接口通路層雖然屬于“層”,但它們并不單獨釋出,而是使用Jar包類庫的方式提供給其它服務調用,是邏輯上的層。服務元件的構成大都是按照業務領域劃分的,隻有一個除外,就是“B2C網站”,B2C網站由于是面向終端使用者的高并發電子商務網站,為了處理高并發,我們将其拆分為兩個業務領域(也可以拆分成多個業務領域,看實際并發量),分别是使用者賬戶和産品。使用者浏覽産品并購買,這是電子商務網站最基本的兩個領域。其中産品浏覽等功能由更底層的産品服務提供,使用者賬戶功能由會員服務提供。還有一些功能,比如推薦商品可能是由其它服務提供,這些可以在控制層直接組合這些服務。
l 服務架構
服務架構采用典型的“服務系統資料庫”模式,系統資料庫使用redis。服務元件在啟動時将自己注冊進服務系統資料庫,web伺服器或api 網關在通路服務時查詢服務系統資料庫得到服務的uri,然後調用某服務接口。
淘寶的dubbo使用的是zookeeper作為服務系統資料庫,之是以使用zookeeper,主要是使用它的負載均衡的功能。筆者認為restful接口沒有必要使用zookeeper做負載均衡,可以使用nginx(負載均衡伺服器都可以),是以沒必要選用動态的zookeeper作為系統資料庫,而是使用“redis+心跳監測”機制(redis也可以換為LDAP等)來完成服務注冊和監控失效服務的功能。這個方案至少比dubbo簡單幾個數量級,簡單就是硬道理。
在注冊服務時隻需要注冊nginx伺服器的IP以及服務描述資訊即可。反觀dubbo還要注冊接口進系統資料庫,筆者認為這個沒必要,因為調用一個服務接口的充分必要條件就是知道伺服器的IP即可。至于調用什麼服務接口,肯定在代碼裡已經寫死,目标伺服器必定存在這個服務接口。将服務接口注冊進服務系統資料庫如果是為了監控審計服務的使用情況,那這個功能使用通路日志來實作能做的更好。
總體的分布式拓補結構如下圖:
對于服務叢集來講,看上去像是一個大哥(nginx)帶有衆多小弟的結構。通路某服務群組隻需要通路其nginx伺服器即可,這裡nginx均采用高可用方案,防止單台nginx出現問題。至于高層服務調用底層服務也是直接通路其伺服器組的nginx。這個執行的流程其實和日常生活中的概念很像,如公司内部的執行流也是如此:老闆配置設定任務給部門經理,部門經理配置設定任務給主管,主管配置設定任務給具體的個人(某單台伺服器)。上司們起到的作用也是負載均衡和監控,負載均衡就是把任務可以配置設定給多個人同時執行(并且某個執行失敗自動切換),監控就不必說了就是監控任務完成情況。在我們的方案裡,會專門有個服務去監控所有伺服器的執行情況,很簡單的服務就可以做到。
l 關于分布式事務
如果研究一下EJB就會發現,EJB和微服務的架構基本相同,甚至所有面向服務(SCA、SOA等等)的架構都相差不大。很多人反對EJB,并不是EJB不夠強大,而是它不夠簡單,它給項目帶來的複雜性甚至超過了項目本身(這也是筆者不建議使用dubbo架構的原因)。曾有人說過:你要麼把事情做的盡可能簡單,讓人挑不出毛病;要麼把事情做的盡可能複雜,讓人找不出毛病,EJB就是後者。EJB分布式事務機制實作的很好,可惜的是這種“一刀切”的事務機制,大大降低了web系統的性能,是以幾乎所有面向網際網路的應用都極少使用分布式事務,也就不會采用EJB。網際網路應用本身事務性操作并不多,一些新聞、部落格之類的網站甚至都不需要事務。另外一個方面,即使出現一個或兩個分布式事務應用場景,也可以通過其它手段解決,比如事件機制等等。
在之前的文章中我們也提到過對于分布式事務最佳的政策是盡量避免。如果避免不了将按以下方式實作。
1) 将分布式事務性操作封裝在一個服務中,這個服務使用XA或鍊式分布式事務管理。
2) 可以使用事件機制協調事務管理(具體做法就是事務失敗後發失敗事件到可持久化的消息隊列,然後需復原操作的接口監控此事件并執行復原)。
3) 使用自定義分布式事務管理器管理分布式事務。
筆者設想的自定義分布式事務管理器主要是封裝了流程及分布式事務相關功能,筆者将在其它文章專門讨論。如圖所示,假設有一個事務需要依次調用ABCDE五個接口,我們首先調用分布式事務管理接口建立這條“流程”的執行個體,執行個體中五個接口分别對應五個狀态,調用成功後将該接口對應的狀态設定為“成功”,反之就是“失敗”,流程處理結束後,分布式事務檢查狀态,然後按照一定的政策調用失敗接口的反向操作接口去復原資料(前提條件也是參與分布式事務操作的接口要開發反向操作接口)。這既是一個簡單的流程引擎,也是一個分布式事務協調處理裝置,具體是否有必要做的複雜(比如處理并行流程),還要看實際環境下分布式事務的情況,但筆者認為網際網路前端應用使用簡單流程應該足以應付。
l 開發架構
系統所需的工程,“[ ]”裡面表示工程的名稱。
從圖中不難看出,我們将運維相關前端界面合并為一個前端系統,總體來講前端隻有3種,B2C前端、APP前端、運維前端。把運維前端合并為一個項目有利于加快前期的開發、部署的效率,在後期如果某子系統功能界面太多,可以将前端系統獨立,比如公估系統、客戶管理系統等,獨立後的系統需要使用單點登入,這樣就可以在各個系統之間免登陸切換。
開發環境:
編碼:UTF-8
工具:Myeclipse 10
SVN:Site-1.8.22
Web伺服器:Tomcat7
JDK: JDK1.7、 Java EE 5
開發環境:Maven 3
開發技術選型:
表現層:Bootstrap+Html+Jquery
MVC架構:Spring MVC 3.2
安全架構:Spring security 3.2
Rest接口實作:Spring MVC Rest
持久層:Mybatis3.2