華為架構(gòu)師8年經(jīng)驗談:從單體架構(gòu)到微服務的服務化演進之路

華為架構(gòu)師8年經(jīng)驗談:從單體架構(gòu)到微服務的服務化演進之路

本文根據(jù)第62期線上分享整理而成,文末還有書送哦~

本次分享的大綱如下:

傳統(tǒng)應用開發(fā)面臨的挑戰(zhàn)

服務化實踐

服務化不是銀彈

服務化架構(gòu)的演進方向

一 、傳統(tǒng)應用開發(fā)面臨的挑戰(zhàn)

挑戰(zhàn)1-- 研發(fā)成本高

主要體現(xiàn)在如下幾個方面:

代碼重復率高

在實際項目分工時,開發(fā)都是各自負責幾個功能,即便開發(fā)之間存在功能重疊,往往也會選擇自己實現(xiàn),而不是類庫共享,主要原因如下:

從技術架構(gòu)角度看,傳統(tǒng)垂直架構(gòu)的特點是本地API接口調(diào)用,不存在業(yè)務的拆分和互相調(diào)用,使用到什么功能就本地開發(fā),非常方便,不需要過度依賴于其它功能模塊;

從考核角度來看,共享很難推行。開發(fā)只需要對自己開發(fā)的模塊交付質(zhì)量負責,沒有義務為他人提供并維護公共類庫,這個非常耗費成本;

時間依賴很難把控:對于公共類庫的使用者而言,依賴別人提供此功能,但是功能提供者可能有更重要的事情在做,提供時間無法滿足使用者。與其坐等別人提供,還不如自己開發(fā)效率高;

跨地域、跨開發(fā)小組協(xié)調(diào)很困難,業(yè)務團隊可能跨地域研發(fā),內(nèi)部通常也會分成多個開發(fā)小組,各開發(fā)小組之間的協(xié)調(diào)和溝通成本非常高。

需求變更困難

代碼重復率變高之后,已有功能變更或者新需求加入都會非常困難,以充值繳費功能為例,不同的充值渠道開發(fā)了相同的限額保護功能,當限額保護功能發(fā)生變更之后,所有重復開發(fā)的限額保護功能都需要重新修改和測試,很容易出現(xiàn)修改不一致或者被遺漏,導致部分渠道充值功能正常,部分存在Bug的問題,示例如下:

無法滿足新業(yè)務快速創(chuàng)新和敏捷交付

挑戰(zhàn)2-- 運維效率低

在傳統(tǒng)的MVC架構(gòu)中,業(yè)務流程是由一長串本地接口或者方法調(diào)用串聯(lián)起來的,臃腫而冗長,而且往往由一個人負責開發(fā)和維護。隨著業(yè)務的發(fā)展和需求變化,本地代碼在不斷的迭代和變更,最后形成了一個個垂直的功能孤島,只有原來的開發(fā)者才理解接口調(diào)用關系和功能需求,一旦原有的開發(fā)者離職或者調(diào)到其他項目組,這些功能模塊的運維就會變得非常困難:

當垂直應用越來越多時,連架構(gòu)師都無法描述應用間的架構(gòu)關系,隨著業(yè)務的發(fā)展和功能膨脹,這種架構(gòu)很容易發(fā)生腐化。

測試、部署成本高:業(yè)務運行在一個進程中,因此系統(tǒng)中任何程序的改變,都需要對整個系統(tǒng)重新測試并部署

可伸縮性差:水平擴展只能基于整個系統(tǒng)進行擴展,無法針對某一個功能模塊按需擴展

可靠性差:某個應用BUG,例如死循環(huán)、OOM等,會導致整個進程宕機,影響其它合設的應用

如何解決傳統(tǒng)單體架構(gòu)面臨的挑戰(zhàn)?

解決對策:1、拆分 2、解耦 3、透明? 4、獨立 5、分層。

拆分:對應用進行水平和垂直拆分,例如商品中心、計費中心、訂單中心等。

解耦:通過服務化和訂閱、發(fā)布機制對應用調(diào)用關系解耦,支持服務的自動注冊和發(fā)現(xiàn)

透明:通過服務注冊中心管理服務的發(fā)布和消費、調(diào)用關系

獨立:服務可以獨立打包、發(fā)布、部署、啟停、擴容和升級,核心服務獨立集群部署

分層:梳理和抽取核心應用、公共應用,作為獨立的服務下沉到核心和公共能力層,逐漸形成穩(wěn)定的服務中心,使前端應用能更快速的響應多變的市場需求

二、服務化實踐

服務的訂閱發(fā)布機制

它的核心理念是實現(xiàn)服務消費者和服務提供者的解耦,讓服務消費者能夠像使用本地接口一樣消費遠端的服務提供者,而不需要關心服務提供者的位置信息,實現(xiàn)透明化調(diào)用。

關鍵技術點:服務的訂閱、發(fā)布機制、服務的健康狀態(tài)檢測和高HA。

常用的服務注冊中心有Zookeeper、ETCD,以及基于數(shù)據(jù)庫的配置中心。

大家在技術選型的時候,需要根據(jù)自己的業(yè)務實際情況進行選擇。例如超大規(guī)模集群,服務實例數(shù)超過10W,Zookeeper就會存在性能問題。

現(xiàn)在開源的分布式配置服務很多,如無特殊需求,建議選擇開源方案。

服務化實踐-零侵入

實際上,完全的零侵入很難做到,即使是聲明式配置,配置本身也是代碼的一部分,只不過相比于代碼類庫依賴,它不是編譯器依賴。

一種好的做法是,服務的發(fā)布和消費通過聲明式或者注解的方式,而不是直接調(diào)用服務框架的接口,例如Thrift??蛻舳诵枰{(diào)用Thrift提供的類庫訪問服務端,這就是代碼API級的依賴,對業(yè)務代碼侵入比較大。

一種比較成熟的實踐是 利用Spring的擴展機制,通過XML的方式實現(xiàn)服務的發(fā)布和消費。

服務化實踐-容錯和路由

單體應用服務化之后,通常采用分布式集群的部署模式。

這會帶來兩個問題:

服務如何路由;

遠端服務訪問失敗之后,如果進行容錯。

大部分的容錯和路由策略可以抽象到分布式服務框架中,通過策略配置的方式提供給用戶使用,降低用戶的開發(fā)成本。

從業(yè)務擴展性角度看,服務框架通常會提供擴展點,供業(yè)務做路由和容錯定制。例如,業(yè)務希望根據(jù)手機號碼和地市進行路由:

服務化實踐-本地短路策略

在電信行業(yè)中,小機還是很普遍,應用通常會合設,例如服務提供者和消費者部署到同一臺主機上。

為了提升性能,降低時延,往往會提供本地短路策略,具體策略如下:

服務化實踐-多樣化調(diào)用方式

服務的調(diào)用方式,主要有三種:同步服務調(diào)用、異步服務調(diào)用、并行服務調(diào)用。最常用、簡單的就是同步服務調(diào)用。

異步服務調(diào)用的工作原理如下:

詳細步驟如下:

消費者調(diào)用服務端發(fā)布的接口,接口調(diào)用由分布式服務框架包裝成動態(tài)代理,發(fā)起遠程服務調(diào)用;

通信框架異步發(fā)送請求消息,如果沒有發(fā)生I/O異常,返回;

請求消息發(fā)送成功后,I/O線程構(gòu)造Future對象,設置到RPC上下文中;

用戶線程通過RPC上下文獲取Future對象;

構(gòu)造Listener對象,將其添加到Future中,用于服務端應答異步回調(diào)通知;

用戶線程返回,不阻塞等待應答;

服務端返回應答消息,通信框架負責反序列化等;

I/O線程將應答設置到Future對象的操作結(jié)果中;

Future對象掃描注冊的監(jiān)聽器列表,循環(huán)調(diào)用監(jiān)聽器的operationComplete方法,將結(jié)果通知給監(jiān)聽器,監(jiān)聽器獲取到結(jié)果之后,繼續(xù)后續(xù)業(yè)務邏輯的執(zhí)行,異步服務調(diào)用結(jié)束。

并行服務調(diào)用,目的是為了提升服務調(diào)用的并行度,降低E2E時延。

服務化實踐-高性能、低時延

服務框架的性能,主要強調(diào)三個要素:1、I/O通信;2、序列化框架;3、線程調(diào)用模型。

如果使用Java語言,I/O框架推薦 Netty。

序列化框架推薦:Thrift、Avro序列化框架、PB等。線程調(diào)度模型建議參考Reactor。

一種線程模型的參考實現(xiàn)方式:Netty的線程模型

無鎖化串行設計理念

服務化實踐-故障隔離

故障隔離非常重要,由于經(jīng)常會采用同步服務調(diào)用模式,核心服務和非核心服務共用同一個線程池和消息隊列,非核心服務處理慢往往會阻塞核心服務,導致雪崩現(xiàn)象。

故障隔離的核心技術點如下:

1. 支持服務部署到不同線程/線程池中

2. 核心服務和非核心服務隔離部署

服務化實踐-服務治理

隨著業(yè)務規(guī)模的不斷擴大,小服務資源浪費等問題逐漸顯現(xiàn),需要能夠基于服務調(diào)用的性能KPI數(shù)據(jù)進行容量管理,合理分配各個服務的資源占用,提高機器的利用率。

線上業(yè)務發(fā)生故障時,需要對故障業(yè)務做服務降級、流量控制、流量遷移等,快速恢復業(yè)務。

隨著開發(fā)團隊的不斷擴大,服務的上線越來越隨意,甚至發(fā)生功能相同、服務名不同的服務同時上線。上線容易下線難,為了規(guī)范服務的上線和下線,在服務發(fā)布前,需要走服務預發(fā)布流程,由架構(gòu)師或者項目經(jīng)理對需要上線的服務做發(fā)布審核,審核通過的才能夠上線。

為了滿足服務線下管控、保障線上高效運行,需要有一個統(tǒng)一的服務治理框架對服務進行統(tǒng)一、有效管控,保障服務的高效、健康運行。

服務治理是分布式服務框架的一個可選特性,盡管從服務開發(fā)和運行角度看它不是必須的,但是如果沒有服務治理功能,分布式服務框架的服務SLA很難得到保障,服務化也很難真正實施成功。

從架構(gòu)上看,分布式服務框架的服務治理分為三層:

第1層為服務治理展示層,它主要由服務治理Portal組成,提供可視化的界面,方便服務運維人員進行治理操作。

第2層為服務治理SDK層,它主要由如下幾部分組成:

服務治理元數(shù)據(jù):服務治理元數(shù)據(jù)主要包括服務治理實體對象,包括服務模型、應用模型、治理組織模型、用戶權限模型、數(shù)據(jù)展示模型等。元數(shù)據(jù)模型通過Data Mapper和模型擴展,向上層界面屏蔽底層服務框架的數(shù)據(jù)模型,實現(xiàn)展示層和服務框架的解耦,元數(shù)據(jù)也可以用于展示界面的定制擴展;

服務治理接口:服務治理Portal調(diào)用服務治理接口,實現(xiàn)服務治理。例如服務降級接口、服務流控接口、服務路由權重調(diào)整接口、服務遷移接口等。服務接口與具體的協(xié)議無關,它通?;诜植际椒湛蚣茏陨韺崿F(xiàn),可以是Restful接口,也可以是內(nèi)部的私有協(xié)議;

服務治理客戶端類庫:由于服務治理服務本身通常也是基于分布式服務框架開發(fā),因此服務治理Portal需要集成分布式服務框架的客戶端類庫,實現(xiàn)服務的自動發(fā)現(xiàn)和調(diào)用;

調(diào)用示例:客戶端SDK需要提供服務治理接口的參數(shù)說明、注意事項以及給出常用的調(diào)用示例,方便前端開發(fā)人員使用;

集成開發(fā)指南:服務治理SDK需要提供集成開發(fā)指南,指導使用者如何在開發(fā)環(huán)境中搭建、集成和使用服務治理SDK。

第3層為后臺服務治理服務層:它通常由一組服務治理服務組成,可以單獨部署,也可以與應用合設??紤]到健壯性,通常選擇獨立集群部署。治理服務的可靠性由分布式服務框架自身來保證,治理服務宕機或者異常,不影響業(yè)務的正常使用。服務治理服務通常并不隨服務框架發(fā)布,治理服務是可選的插件,單獨隨服務治理框架交付。

服務化實踐-高可靠性

關鍵技術點設計如下:

服務無狀態(tài)設計

服務注冊中心集群,宕機不影響業(yè)務運行

服務提供者集群,集群容錯屏蔽服務提供者故障

服務健康狀態(tài)檢測,基于時延等性能KPI指標

服務治理中心集群,宕機不影響業(yè)務運行

服務級故障隔離

核心服務獨立部署和集群

跨機房路由和異地容災

三、服務化不是銀彈

服務化會帶來很多收益,但是它卻不是銀彈。

服務化不是銀彈-時延問題

在服務化之前,業(yè)務通常都是本地API調(diào)用,本地方法調(diào)用性能損耗較小。服務化之后,服務提供者和消費者之間采用遠程網(wǎng)絡通信,增加了額外的性能損耗。

服務化不是銀彈-問題定位

在分布式環(huán)境下,如何高效的進行問題定界定位和日志檢索

服務化不是銀彈-事務一致性

服務化、分布式部署之后,有邏輯關聯(lián)關系的多個數(shù)據(jù)庫操作被打散到集群中各個獨立的服務實例中,引入分布式環(huán)境下的事務一致性問題。

服務化不是銀彈-前后臺直接通信問題

前后臺直接通信問題如下:

存在的問題如下:

客戶端需求和每個微服務暴露的細粒度API不匹配

微服務使用的RPC私有協(xié)議,不是瀏覽器友好或防火墻友好的

微服務難以重構(gòu)。隨著時間推移,我們可能想要更改系統(tǒng)劃分成服務的方式。如果客戶端與微服務直接通信,那么執(zhí)行這類重構(gòu)就非常困難了

服務化不是銀彈-團隊協(xié)作問題

共享服務注冊中心問題:為了方便開發(fā)測試,經(jīng)常會在線下共用一個所有服務共享的服務注冊中心,這時,一個正在開發(fā)中的服務發(fā)布到服務注冊中心,可能會導致一些消費者不可用。

多團隊進度協(xié)同問題:服務提供者和消費者相互依賴問題,開發(fā)依賴、測試依賴等。

接口前向兼容性問題:由于線上的Bug修復、內(nèi)部重構(gòu)和需求變更,服務提供者會經(jīng)常修改內(nèi)部實現(xiàn),包括但不限于:接口參數(shù)變化、參數(shù)字段變化、業(yè)務邏輯變化和數(shù)據(jù)表結(jié)構(gòu)變化。在實際項目中經(jīng)常會發(fā)生服務提供者修改了接口或者數(shù)據(jù)結(jié)構(gòu),但是并沒有及時知會到所有消費者,導致服務調(diào)用失敗

四、未來演進方向-微服務架構(gòu)

微服務的劃分原則是難點,根據(jù)華為的經(jīng)驗:微服務劃分不是一步到位,而是不斷的迭代和演進,最終找到適合自己團隊和業(yè)務的微服務劃分原則。

未來演進方向-基于Docker部署微服務

使用Docker部署微服務的優(yōu)點總結(jié):

一致的環(huán)境:線上線下環(huán)境一致

避免對特定云基礎設施提供商的依賴

降低運維團隊負擔

高性能:接近裸機的性能

多租戶

未來演進方向-云端微服務

利用云平臺的彈性資源調(diào)度,動態(tài)性等,可以實現(xiàn)微服務的Dev&Ops

最后我們一起回顧下服務化的演進歷程:

Q&A

Q1:上面提到服務化缺點的第三條接口變更問題,請問微服務是如何解決這個問題的呢?或者說微服務相比之下什么優(yōu)勢會避免這個問題?

A1:根據(jù)我們團隊的經(jīng)驗,主要從如下幾個方面降低影響:1、微服務的接口就是契約,制定 接口兼容性規(guī)范;涉及到技術和管理兩個層面;2、微服務鼓勵只做一件事情,因此它更加穩(wěn)定;3、基于消費者契約測試,快速發(fā)現(xiàn)兼容性問題。

Q2:微服務架構(gòu)里,分布式事務如何做的,對數(shù)據(jù)一致性要求較高的系統(tǒng)是否適合拆分成微服務,或者說微服務的粒度如何把握?

A2:分布式事務是難點,策略如下:1)如果業(yè)務上能夠承受非強一致性,建議通過事務補償?shù)姆绞阶鲎罱K一致性,可以基于MQ等中間件來實現(xiàn);2)如果是轉(zhuǎn)賬、實時計費、充值等對實時性要求高的,往往選擇強一致性事務,就需要引入TCC等分布式事務框架。無論如何,只要做分布式,事務一致性就會成為問題,跟是否是微服務沒必然關系。

Q3:生產(chǎn)環(huán)境中的服務注冊中心必然是共享的,那如何去做灰度發(fā)布或者A/B Test呢?

A3:一種比較好的服務灰度策略是:1)服務框架提供灰度規(guī)則框架,包括后臺引擎和前臺Portal,由業(yè)務配置灰度規(guī)則;2)分布式服務框架支持灰度規(guī)則推送和業(yè)務自定義路由;3)前端SLB ,例如Ngix做灰度插件,接收灰度規(guī)則。消息從前端門戶接入到后端服務路由,都支持基于規(guī)則的路由分發(fā)策略,實現(xiàn)灰度發(fā)布。

Q4:Netty的無鎖化串行會比有鎖的并行性能更高嗎?有案例嗎?華為現(xiàn)在都是用Docker部署應用嗎?

A4:Netty的無鎖化串行性能問題:1)在實際項目中,線程池爭用模式和串行模式我們都使用過,Netty的無鎖化串行模式性能更高。Docker部署應用:華為的公有云和私有云都支持基于Docker部署應用,由客戶根據(jù)需要自主選擇。

Q5:IO通信是怎么保證每次連接成功的呢?

A5:NIO通信本身并不保證每次連接都成功,它的連接是異步的,你可以根據(jù)如下兩種策略獲得異步鏈接的結(jié)果:1)發(fā)起連接之后主動調(diào)用同步方法等待結(jié)果返回,阻塞式;2)獲取異步連接Future,添加Listener監(jiān)聽器監(jiān)聽連接結(jié)果,這種模式是異步回調(diào),不會阻塞當前線程。

Q6:使用zk作為服務注冊中心,對與某個服務當客戶端連接數(shù)很多時候節(jié)點變化會引起羊群效應,怎么處理這種問題呢?或者說如何避免這種問題呢?

A6:這個問題真是好!通常而言,大家會使用服務注冊中心做服務可用性檢測,如果發(fā)現(xiàn)某個服務節(jié)點不可用,就會將其從注冊中心中刪除。但是,有一種場景是ZK檢測的結(jié)果跟客戶端和服務端實際的連接狀態(tài)不一致。從ZK看,服務提供者可以使用。但是由于服務消費者跟提供者之間的鏈路已經(jīng)中斷,跟ZK的鏈路卻是正常,這種情況下就會出現(xiàn)狀態(tài)不一致問題。所以,只依靠ZK做狀態(tài)檢測還不夠,需要服務提供者和消費者的鏈路層做雙向心跳檢測。

Q7:我現(xiàn)在做的系統(tǒng)是zk做注冊中心服務把地址注冊上去(臨時節(jié)點),客戶端拿地址請求,http的,現(xiàn)在發(fā)現(xiàn)如果是公網(wǎng)調(diào)用的話,對公網(wǎng)資源要求還挺多的,zk公網(wǎng), 應用公網(wǎng);為了減少對公網(wǎng)需求,中間加一層nginx,把nx地址注冊上去,不過又得加個http探測監(jiān)控程序,異常還得刪掉注冊數(shù)據(jù),不知道這種做法是否妥當?

A7:Ng監(jiān)聽ZK注冊的服務提供者URL即可,問題不大。

Q8:用Netty做同通信框架,監(jiān)控上報應該怎么設計更完善?

A8:建議的方式如下:Netty自身不用告警,監(jiān)聽Netty的異常事件,然后通過MQ吐出去,監(jiān)控系統(tǒng)訂閱通信框架的事件主題,實現(xiàn)通信框架和監(jiān)控系統(tǒng)解耦。

Q9:SOA和微服務架構(gòu)的區(qū)別和聯(lián)系是?看起來好像??!

A9:1)?服務拆分粒度:SOA首先要解決的是異構(gòu)應用的服務化;微服務強調(diào)的是服務拆分盡可能小,最好是獨立的原子服務;

2)?服務依賴:傳統(tǒng)的SOA服務,由于需要重用已有的資產(chǎn),存在大量的服務間依賴;微服務的設計理念是服務自治、功能單一獨立,避免依賴其它服務產(chǎn)生耦合,耦合會帶來更高的復雜度;

3)?服務規(guī)模:傳統(tǒng)SOA服務粒度比較大,多數(shù)會采用將多個服務合并打成war包的方案,因此服務實例數(shù)比較有限;微服務強調(diào)盡可能拆分,同時很多服務會獨立部署,這將導致服務規(guī)模急劇膨脹,對服務治理和運維帶來新的挑戰(zhàn);

4)?架構(gòu)差異:微服務化之后,服務數(shù)量的激增會引起架構(gòu)質(zhì)量屬性的變化,例如企業(yè)集成總線ESB(實總線)逐漸被P2P的虛擬總線替換;為了保證高性能、低時延,需要高性能的分布式服務框架保證微服務架構(gòu)的實施;

5)?服務治理:傳統(tǒng)基于SOA Governance的靜態(tài)治理轉(zhuǎn)型為服務運行態(tài)微治理、實時生效;

6)?敏捷交付:服務由小研發(fā)團隊負責微服務設計、開發(fā)、測試、部署、線上治理、灰度發(fā)布和下線,運維整個生命周期支撐,實現(xiàn)真正的DevOps。

總結(jié):量變引起質(zhì)變,這就是微服務架構(gòu)和SOA 服務化架構(gòu)的最大差異。

Q10:如果要將現(xiàn)有單機服務重構(gòu)到微服務,應該考慮哪些問題?數(shù)據(jù)遷移的安全問題怎么解決?有什么實踐方案嗎?

A10:需要考慮的問題如下:1)當前單機應用是否能夠滿足業(yè)務發(fā)展需要,有沒有必要做服務化改造和分布式部署;2)評估遷移的工作量,以及人員技能培訓等。3)自研服務框架還是使用開源的方案。

數(shù)據(jù)遷移安全問題:如果內(nèi)網(wǎng),通常不會涉及到復雜的安全控制問題;如果跨公網(wǎng),建議加入API? Gateway統(tǒng)一做安全管控。

實踐方案:公開的資料,可以參考淘寶的服務化實踐、京東的服務化實踐等。其實華為也有,不過遺憾的是目前政策不允許公開出來。

Q11:麻煩李老師介紹下你們?nèi)A為內(nèi)部基于netty做socke通信的協(xié)議設計的最佳實踐。

A11:這個問題很大,簡單介紹下思路。在11年和13年的時候我分別主持設計了華為基于Mina和Netty的統(tǒng)一NIO通信框架。設計要點如下:1)要熟悉Netty的線程調(diào)度模型、常用的類庫等,能夠熟練使用Netty;2)NIO通信框架的分層原則,哪些該做、哪些不該做,需要識別出來;3)擴展點,預留足夠的擴展點給上層應用協(xié)議棧做擴展;4)可以內(nèi)置配置化的安全策略、握手認證、心跳檢測等機制;5)可服務性設計,包括日志、性能KPI指標等。

作者介紹 ?李林鋒

從事華為軟件PaaS平臺的架構(gòu)設計和開發(fā)工作,8年多NIO、平臺中間件領域設計、開發(fā)和運維經(jīng)驗,精通NIO通信框架、分布式服務框架、PaaS平臺等;

參與設計和開發(fā)某網(wǎng)關平臺;

曾獲得公司總裁技術創(chuàng)新獎;

《分布式服務框架原理與實踐》作者。

好書相送

在本文評論區(qū)留下足以引起共鳴的真知灼見,并在本文發(fā)布后24小時之內(nèi)成為點贊數(shù)最多的前2名,即可獲得李林鋒老師所著的書籍一本哦~

特別鳴謝博文視點(www.broadview.com.cn)為本次活動提供圖書贊助。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容