業務系統成功微服務化改造的實施步驟

天府雲創發表於2018-01-12

1. 篇首語

業務系統是任何一個使用者產品的必須組成,充當著一個門面的角色,使用者的輸入就是這個系統需要維護的,資料存取是整個系統的核心。例如,廣告業務系統的輸入是廣告主的投放約束、定向條件,微博業務系統的輸入是短文字、圖片等。

 

在應用發展初期或者規模不大的情況下,有非常簡單的實現方案,LNMP、JSP、PyWeb都是你能隨口說出來的詞,如果用某種架構方式來描述,那就可以稱做單體模式(Monolithic,Martin Flower大神所提出的,後面還會介紹),而這些技術都是單體模式的成熟解決方案,它們可以使你工作在“應用層”的最頂端,各種中介軟體、框架能讓你省心地幹好業務,開發人員可以通過“模組化”的手段來管理業務系統的複雜度,使他們之間解耦、複用。簡單來說,這個單體就是如下這種層次劃分。

 

JAVA程式碼

表示層       前端(HTML+CSS+JS)                        

  |                   |

邏輯層     業務系統(PHP、Java、Python是常用的語言)         

  |                   |                               

資料層      資料庫(MySQL)

 

看起來很簡單,對吧。誠然,業務系統在這裡面還需要做很多,比如快取、SQL優化、資料分割槽、系統安全工作,當然還有對於程式碼的維護和重構。這種工作方式可以很好的工作幾年、甚至十年,但是如果業務發展非常快,在系統複雜度、業務規模、參與人數、程式碼腐化程度都不斷上升的情況下,你會發現整個專案正陷於泥潭,PM/RD/QA/OP經常抱怨:

 


"改個小功能,怎麼要拉這麼多模組?"

"拉模組也就罷了,改的地方多,編譯太慢了。"

"慢也就算了,關鍵不知道怎麼改,這程式碼太醜陋了,算了,為了滿足PM的排期需要,湊合來吧。"

"湊合來了,QA發現bug,返工再返工,延期再延期。"

"上線了,oh my god,報case了,效能有問題,原來是依賴的模組訪問資料庫用了for迴圈select。"


 

透過現象看本質,我總結了來看就這三點問題:

 

1)業務邏輯複雜耦合,開發維護成本高。

系統複雜度、規模、參與人數都和腐化程度成正比,單純的靠模組化,後期來看會存在個別模組成為”大怪物“,臃腫不堪,粒度過粗,難以複用。

 

2)交付效率和質量低。

在敏捷和持續整合方法論、實踐大行其道的現今,迭代的按期交付率、單測覆蓋率都不盡如人意,線上問題頻發。

 

3)非功能需求不達標。

非功能需求指標特指效能、可用性、可擴充套件性等方面,程式碼的腐化和缺少維護、重構,以及沒有程式碼潔癖的人汙染下,必然導致效能逐漸下降,甚至出現不同資源競爭的短板效應,造成整個系統crash,同時一個大怪物的伸縮性較差,不能隨意橫向擴充套件某個細分功能點。

 

我想任何人做架構都需要秉承“業務需求決定技術演化路線”的思路,那麼這些暴露出來的現狀和問題都驅動系統去轉型,在系統和人之間找到一種最佳的合作模式,匹配已有的生產力和生成關係。正如軟體開發學泰斗Kent Beck所說的:

“Design is there to enable you to keep changing the software easily in the long term” ,即“變化發生時設計被破壞的程度”

 

放眼業界,面對複雜的、大規模的、多人協作完成的業務系統,如何管理好這個複雜度,有很多方法,模組化、OSGI、傳統服務化SOA等等,當今最佳實踐的趨勢還是服務化。而微服務是最近火熱起來的概念,有些人肯定覺得這不就是炒作嘛,但是不管黑貓白貓能抓耗子就是好貓,所以解決問題是重點,不要刻意去批評一些名字,那麼本文要重點介紹的就是——如何做微服務化轉型和改造。

 

在接下來講之前,要重點宣告,本文並不是推崇服務化,不鼓勵單體模式,相反而是相當肯定和支援單體模式,它模組依賴簡單、一個釋出包、部署於一個容器都使得構建應用非常的簡單,在體量還不大的情況下,首先應該解決的是搭建好一套絕對穩定的基於模組化的平臺,待體量逐漸長大,再去根據實際需要進行拆分,團隊也隨之變化(facebook的單體持續了非常長的時間,一是人員素質高,二就是基礎平臺建設的非常好)。再下個階段體量大到飽受單體模式之苦,也應該先建設平臺化服務,建設好之後,先按照大的粒度進行拆分,逐步再微服務化,否則,直接上服務化、甚至下面要說的微服務都是非常危險的,鮮有成功案例。

 

2. 微服務化改造

做改造一般要經歷三個步驟:

第一、技術選型決策

第二、架構設計規劃

第三、落地實施應用。

 

下面依次展開三個部分,重點介紹前兩個步驟,有了這兩個,落地應用也就順水推舟的好做了。

 

2.1 技術選型決策

 

2.1.1 選擇微服務化方式

選擇服務化,眾所周知就是SOA嘛,這是一種架構風格,重點在原則、理念、方法論等高思維層次上,對於工具、框架、解決方案沒有做強制限制,ESB、websercie(基於WSDL和SOAP/BPEL)這兩種是企業中流行的,也是過去一直引領SOA的技術領頭羊,但是隨著網際網路應用的發展,在敏捷快速迭代、高可用、高效能、高併發等方面要求越來越高,傳統的SOA並不適合這種場景。那麼,現在的網際網路流行的實現方式是什麼呢?一種最佳的實踐方式就是微服務化(Micro Service)。[配圖1]

\

微服務就是一種SOA的實現方式而已,更加側重於在服務的細分演化,是指引服務的具體落地方案層面的一種實踐方式。過去很多網際網路公司在實踐,你可以把淘寶的dubbo、HSF,navi-rpc服務化框架看做比傳統SOA更適用、更貼近微服務化實現的服務化框架,依賴這些框架可以方便的做服務化。這個趨勢被Martin Flower大神所發現,並且提出了,你們這些不都是在做“微服務”嘛。

Martin對微服務這個術語(terminology)的解釋是:

 

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.


簡而言之,微服務化就是以一系列小的服務來開發支撐一個應用的方法論,服務獨立在自己的程式中,通過輕量級通訊機制互動(通常是走HTTP協議)。這些服務是圍繞著業務上的組織結構來構建的,全自動的、獨立部署。幾乎看不到中心化的服務管理基礎設施,可以使用不同的程式語言和資料儲存技術來實現不同的服務。

 

在簡單的一種理解來自於一本書《Building Microservices》(Sam Newman, O’Reilly Media),Microservices are small, autonomous services that work together. 微服務化就是一堆小而自治的服務,讓他們一起工作起來。

 

相比於傳統的SOA,Martin的總結特點可以參考他的部落格還有視訊(Youtube連結),一共是9個特點,這裡不想贅述,而是說說我個人的理解,微服務化的特點在於:

 

1、模組即服務

微服務中的元件在邏輯或者物理層次更趨於細分,這個細分不是極致的,而是一種粒度適中的選擇,通常這些元件在前期可以是一些模組,但是當需要時,例如業務上需要拆分獨立,或者非功能需求上需要擴容等,都可以靈活的拆解出來。這個特點非常重要,因為業務系統中模組化實踐,隨著軟體規模的變大,很容易繞過障礙而使得不同模組耦合、依賴關係複雜,這種紀律性很難保證,從而削弱模組化的結構、降低了團隊的生產力(敏捷開發和持續交付越來越難做,部署起來太龐大了大家的開發士氣不高,而且痛苦),很快的這個模組就會變成一個大雜燴,而服務可以做到天然的壁壘,僅僅通過交換契約(通常是API或者proto)來做互動,這是一個演化的過程,不僅有利於分而治之,到達複用的目的,同時老系統也可以灰度的改造剝離。

 

2、獨立自治

這意味著服務是獨立開發,獨立測試,獨立釋出,獨立部署,獨立運維的。某個細分團隊負責整個生命週期管理,這就是”康威定律(Conway’s law)”的通俗解釋,官方解釋是“一個組織的設計成果,其結構往往對應於這個組織中的溝通結構”,服務的規劃不就是多人、跨團隊協作的溝通模式嘛。好處在於摒棄原來的火車模型(所有模組一起釋出部署),擁抱獨立快跑,這也更好的支援了敏捷和持續整合的方法實踐。同時去除了牽一髮而動全身的問題,單一職責的來進行修改需求或者重構一個點,開發和構建方便,不影響整個產品的功能,一個bug不會crash掉整個產品,針對不同的型別,區分計算密集型還是I/O密集型,區分業務上更好使用關係型還是NoSQL,區分2/8原則、即80%經常修改的服務獨立出來自成一家,區分短板功能、針對瓶頸可以做水平擴充套件、避免資源競爭,甚至可以區分技術棧、突破語言限制。最後,這也是和第一點遙相呼應的,獨立的服務可以實現非常大程度上的複用,服務之間依賴輕量級的介面,而不是模組。

 

3、去中心化的資料管理

在單體模式中,一個應用面對一套資料庫,資料庫可以按照物理拆分,進行shard分割槽,或者按照邏輯庫隔離,不同的業務路由到不同的庫,同時做一主多從等結構上的設計。這些原則在微服務中仍然適用,只不過微服務在服務拆分的同時,也需要將資料庫分離,獨立的服務維護獨立的資料庫,這對資料庫也是減負,同時技術選型、SLA保證都會區分開來,把精力留給那些重要的業務資料庫,進行分級的對待,而不能像以前一樣一視同仁,一個不重要的邏輯庫的bad SQL慢查詢,阻塞了其他正常的查詢,這是完全可以避免的。

 

4、輕量級的通訊協議

傳統的SOA使用ESB或者Webservice這種重量級的解決方案,微服務推薦使用一些更輕的解決方案,要通用性,可以用Restful架構,走HTTP通道,支援Json序列化協議;要高效能,可以考慮一些高效能的I/O模型,例如epoll、Actor等,可以直接走tcp通道,使用protobuf序列化協議,同時保持長連線(這裡有一個例子Navi-pbrpc就是一個這樣的具體落地框架);要非同步,用可靠持久的RabbitMQ或者高效能的ZeroMQ來做P2P、Pub/Sub、廣播broadcast訊息通訊。而這種輕量級還需要體現在程式碼呼叫中,模組化直接通過函式、方法呼叫即可,服務化後能不能在API層面做到無侵入,無縫的切換、簡單配置,這些都是服務化框架要支援的。

 

5、為失敗設計

服務化呼叫從程式內in-process的呼叫,轉變為跨程式的分散式呼叫,這種由分散式特性引起的天然不可靠性,需要變為相對可控。也就是服務間的通訊要假設不會成功,為失敗處理。異常的傳遞,能否透過RPC,在呼叫方本地還原,就像函式、方法呼叫一樣?一個點、或者服務的處理錯誤率到了一個閾值,為了不影響整個產品,要做錯誤隔離,可以考慮熔斷(circut break)、艙壁隔離模式、限流、回退等手段,最後還有一個冪等性問題,重試的呼叫會不會對業務造成影響,這個要具體問題具體分析了。

 

6、基礎交付設施自動化

這個特點是整個微服務中的最大亮點,包括持續整合CI、持續交付CD和PaaS平臺的結合。微服務在細分的背景下,在project結構,物理結構上都提高了一個複雜度,如果還要做到提高軟體交付鏈路的整體效率,就需要在基礎交付上做一個大的轉變,因此DevOps文化,讓每個人都參與交付,在規範的流程和標準的交付(例如,標準的容器)下,同時在PaaS服務提供商的幫助下,完成一個服務的自動部署釋出。

 

任何事情都是兩面的,有好的優點,當然會存在弊端,微服務的缺點我的理解如下:

 

1、分散式呼叫造成的效能、延遲問題。(可以採取的措施包括粒度適中、批量、高效能RPC、非同步通訊等)

 

2、可靠性不好保證。(剛才提到的為失敗設計可以解決)

 

3、資料一致性難以保證。(看各自的業務,確保最終一致性即可,實際上大多數網際網路產品很少不用事務;但是我目前所工作的商業產品領域,是需要事務的,除了不推薦的兩段式提交,還可以引入仲裁者、補償措施來解決分散式事務問題,問題可以單獨開一篇文章介紹了,這裡就不展開了)

 

4、整體複雜度提升。服務多、依賴多、呼叫多、契約如何管理、監控如何做,呼叫鏈上怎麼確定哪個點有問題,服務的SLA保障、效能、錯誤率、告警、這麼多服務如何整合測試、交付容器如何上線等等問題。(通過服務治理可以有效降低微服務化複雜造成的低效,轉為推動工程生產力的高效進化,同時基礎交付設施的自動化可以加速研發效率,選擇了微服務就等於選擇了成本優先戰略,投入的成本都是為了未來業務的更好發展,避免J-curve曲線式的研發模式,只有在體量大,基礎設施包括服務化框架、治理能力完善的基礎上,加上流程、規範以及工具和技能的輔助下,才可以真正發揮服務化的威力,否則只有自討苦吃)

 

2.1.2 微服務化框架和治理模型

架構方式、原則達成了共識,你再往下看。虛的說完了來點乾貨,來介紹下我所在team實踐的微服務化,最核心的就是微服務化框架,業界流行的阿里的框架dubbo以及淘寶內部的HSF,Navi-rpc都可以看做微服務化框架的雛形,加上服務治理中心的管理、基礎交付設施的保障就可以構成完整的一套微服務框架。我們的框架(暫時僅內部使用)整體架構如下圖,他由服務釋出者、呼叫者和治理中心三者組成,屬於標準的協調者模式。[配圖2]

\

生產者中服務邏輯在Spring或者Guice等IoC框架的bean中,由IoC容器託管,為了符合模組即服務的思想,在框架層級實現了一套可插拔元件的引擎,去實現元件的掃描,需要暴露服務的釋出出來,依賴別的服務的,通過位元組碼技術生成Rpc呼叫代理Stub,形成了一個基於元件的容器,通過JSR315這個規範的SPI實現對接到J2EE容器,下面的消費者結構相同。

 

在服務啟動後,首先會第一步註冊自己到服務治理中心,上傳自己的契約、版本上去,治理中心如果通過檢查就釋出出去,之後和治理中心通過長連線協議(我們採用websocket,因為現成、簡單)做一個訂閱釋出的通道,可以供收集狀態,推送服務Endpint的變更;服務消費者可以去治理中心或者Maven倉庫獲取契約、SDK,治理中心推送Endpoint下來,供路由進行Rpc呼叫,通過消費者也通過長連線協議來進行狀態和統計資訊的上報,供治理中心進行分析決策和反饋。


服務治理中心為了保證高可用性,通常使用Zookeeper這個流行的開源的基於Paxos的方案,當然最近漸漸流行起來的kebernetes的etcd是基於Raft的叢集共享資料、也可以做服務的發現的解決方案。

 

隨著這種分散式呼叫越來越頻繁,就需要服務治理能力越來越強,否則就是一張混亂的、無序的Rpc呼叫的網,無法管理複雜度。

 

這裡建立了服務治理的模型,在下圖中的服務治理中心來實現,模型從這樣幾個角度來考慮如何治理服務,包括通訊、契約、版本、監控、安全、交付等角度,依託服務治理中心,有了這套基礎設施保駕護航,服務化就可以真正做到提高研發效率、提供優雅的開發體驗。[配圖3]

\

在基礎交付設施自動化上,如下圖所示,體現在自動化、容器化交付這個流程中,在平臺化的背景下把團隊思維轉換為DevOps式的,依託Docker和k8s完成了PaaS平臺的對接,同時和QA一起協作完成持續交付流程的建立。[配圖4]

\

2.2 架構設計規劃

這裡所指的架構,特指組織、服務的架構設計,非部署和程式碼架構。

 

下面我要介紹的,都是扣題,是已有系統的服務化改造,是一個已經存在的、複雜的、體量大的業務系統。

 

做架構設計規劃,主要分為步驟:

1整體架構設計

2業務領域抽象、建模

3服務規劃與層次劃分

4服務內流程、資料、契約(介面)定義和技術選型。

 

這裡主要介紹前三個步驟,第四個偏向於個例,同時需要強結合業務需求、特點分析解決,這裡不做詳細展開。

 

2.2.1 整體架構設計

還記得文章開說所說的單體模型嗎?在一個複雜的、規模大的業務系統中,使用微服務化方式實現,就需要從上到下的來做整體架構,下面這張圖是我所在的商業產品的業務端到檢索端的架構圖。[配圖5]

\

共分為5個層次。

第一層,模組化組裝。是各個投放產品的門面,各個投放產品可以通過搭積木式的方式,組裝下層服務,就可以完成一個面向使用者的功能,最常見的SpringMVC技術、Java設計模式中的facade模式就屬於應用到這一層的一些點。

 

第二層,計算服務層。服務化也就是在這個層次上展開的,每一個小圓圈都是一個微服務,這是整個服務化的核心,各個服務圈出來的都是一個個服務簇,比如投放管理一個簇,報告報表一個簇。

 

第三層,資料儲存層。會針對各個業務拆分,按照物理庫或者邏輯庫進行隔離。

 

第四層,廣告傳輸層。將多shard的MySQL寫入的廣告增量實時傳輸到檢索端,形成一條增量流(incremental data stream),我們通過模擬為MySQL的一個從庫來捕獲解析binlog實現,將binlog增量對映為語言級別的抽象型別,供下游使用,下面一層就是一個資料接收方,其他的還包括一些MQ訂閱方(如匯入kafka、RMQ、ZeorMQ等),HDFS儲存等,這樣就形成了業務系統的資料快速、高效、實時傳輸的目的。

 

第五層,檢索端。是廣告投放系統的核心,根據媒體環境、使用者特徵匹配最佳的廣告,進行創意的投放,你所看到了圖片、H5、flash廣告都是這套系統響應的,可以做到千人前面,最佳化廣告主ROI與使用者體驗的折衷。

 

2.2.2 業務領域抽象建模

技術是為業務服務的,沒有了業務,純粹的講技術都是紙上談兵,解決問題是所有技術的出發點,微服務化也不例外。服務於業務,就需要對業務有深刻的理解,技術才能形成良好的輸出。

 

有了前一步的整體架構規劃,下一步就是計算服務層中的微服務如何規劃的問題,這部分最為複雜,需要深入到產品業務中。拍腦袋規劃當然可以,這叫做經驗直覺主義,我認為經驗主義缺少規範化的表達和標準化的設計,面對未來的修改需求,其架構的生命力不會很強。所應該站在更高的視角上嘗試解決,首先就是要規範化需求表達,下圖就是一個投放實施的表達,使用巴克斯正規化(BNF正規化)表達,將投放實施分為受眾、媒體、場景等定向的選擇,每種定向又分為多個約束條件,逐層深入,這個規範是所有已有產品的萃取,在新產品的打造中需要遵守的,一般會和產品經理一起打造。[配圖6]

\

 然後各個投放產品進行的功能矩陣劃分的標準化設計,以這些為基礎,就可以有理有據的進行服務規劃,抽象分解出來的服務域高內聚,職責非常清晰,服務內的實體也是建模的,如下圖所示,每個包都是一個微服務。[配圖7]

 \

2.2.3 服務規劃與層次劃分

基於對業務的抽象分解,在計算服務層內部,就可以進行更加細分的層次規劃,先是垂直拆分為展現層、計算層、資料資源3大縱層,核心的計算層又細分為3個層次,包括業務流程處理層,通過組裝下層服務完成功能;業務邏輯元件是自包含,跨產品線、高度複用的元件;下面公共服務元件是一些通用服務。然後水平劃分為多個服務簇。如下圖所示。[配圖8]

\

按照之前的服務規劃,將各個微服務安置其中,最上層的web-ui和api服務負責和前端js以及客戶端(安卓或者iOS)API打交道,中間例如推廣管理作為一個業務流程處理元件的workflow,可以呼叫下面的微服務進行組織,完成一個投放流程的業務場景。所有這些服務都是通過分散式服務化框架來進行通訊、治理的。

 

2.3 落地實施應用

下面是一個已有產品改造的案例,比如一個報表服務簇,過去是一個大單體,現在按照服務化的架構,進行拆分,最為核心的就是中間這個sync-report服務,它從olap engine中查詢資料,然後通過merge字面資料,提供排序,過濾,分頁功能。圍繞sync-report抽取了多個不同維度的快取,保證了核心報表服務的高效能,同時上層,不管是web-ui還是api,都複用sync-report,這樣上層就會很薄,不用再管那些複雜的查詢邏輯,sync-report作為標準、規範的技術解決方案,做到了統一複用與專職專用,加速了研發效率和交付。[配圖9]

\

3. 篇後語

本文所提倡的微服務,是結合作者所在team自身業務特點來說的,適合自身的場景,是建立在團隊人員素質到了,有成熟的基礎設施和框架、中介軟體輔助,流程也規範,包括CI、敏捷等,團隊都做好了準確去做這個轉變,有足夠的能力來實施,微服務化也就是水到渠成的事了。相反,小團隊在前期或者野蠻生長時期,不宜選擇微服務,不但影響效率還帶來額外的複雜度。成長型或者大公司,有成熟的流程、規範、基礎設施、平臺等,要想在整條交付鏈路上加速,就需要投入更多的資源保障微服務化,一切自動化了,能治理了,回頭看來這一切就都是值得的,遠期收益非常可觀。

 

最後要說的是,架構只是標準、骨骼,對微服務的討論不應該讓我們忘記了更重要的問題,驅動軟體專案成功和失敗的重要因素。軟因素如團隊中人的素質,以及他們如何彼此合作、溝通,這都會對是否使用微服務有很大的影響。在純技術層面上來講,應該把重點放在乾淨的程式碼、完善到位的測試,並持續關注架構的演化進步,這才是一個軟體工程師的根本職責。


——本文摘自infoQ《架構師》期刊201606期中《談談後端業務系統的微服務化改造》一文,作者:張旭。

相關文章