微服務(wù)落地實(shí)踐 - 經(jīng)驗(yàn)分享

前言

隨著架構(gòu)設(shè)計(jì)的發(fā)展,微服務(wù)架構(gòu)可以說(shuō)是目前架構(gòu)領(lǐng)域炙手可熱的設(shè)計(jì)理念。在公司,筆者也一直在負(fù)責(zé)系統(tǒng)的服務(wù)化設(shè)計(jì)和開(kāi)發(fā)工作。

今天就來(lái)談?wù)勎⒎?wù)落地實(shí)踐中的一些問(wèn)題。希望對(duì)微服務(wù)設(shè)計(jì)無(wú)從下手的朋友,起到一些參考作用;另外也希望把自己的觀點(diǎn)分享出來(lái),期待與大家一起交流,能夠認(rèn)識(shí)到不足之處。

一、服務(wù)拆分

在落地微服務(wù)之前,我們遇到的第一個(gè)問(wèn)題就是:應(yīng)該如何拆分服務(wù)?

大家知道,關(guān)于如何拆分服務(wù),并沒(méi)有一個(gè)完全通用的法則。不過(guò)有以下幾點(diǎn)要素,我們可以參考。

1、業(yè)務(wù)獨(dú)立性

一般情況下,我們第一時(shí)間都會(huì)考慮到這點(diǎn)。將系統(tǒng)中的業(yè)務(wù)模塊,按照職責(zé)標(biāo)識(shí)出來(lái),每個(gè)單獨(dú)的模塊拆分成一個(gè)獨(dú)立的服務(wù)。

這樣拆分之后,我們的服務(wù)要滿足一個(gè)原則:高內(nèi)聚,低耦合。

比如,我們把一個(gè)系統(tǒng)拆分為商品服務(wù)、訂單服務(wù)、物流服務(wù)。一般情況下,我們修改物流服務(wù)的時(shí)候,并不會(huì)影響商品服務(wù),這就是低耦合的體現(xiàn);那么訂單服務(wù)里面的功能和邏輯,都是圍繞著訂單這個(gè)核心業(yè)務(wù)流程的,就可以說(shuō)它是高內(nèi)聚的。

2、業(yè)務(wù)穩(wěn)定性

我們可以把系統(tǒng)中的業(yè)務(wù)按照穩(wěn)定性進(jìn)行區(qū)分。比如,用戶注冊(cè)、登錄部分,在我司的系統(tǒng)中,這一塊的代碼只要寫(xiě)完,基本就不再會(huì)發(fā)生變動(dòng),那么我就將他們拆為用戶服務(wù)。

同理,還有日志服務(wù)、監(jiān)控服務(wù),這些模塊基本上也都是很穩(wěn)定的業(yè)務(wù),可以考慮單獨(dú)拆分。

3、業(yè)務(wù)可靠性

這里講究的是,將可靠性要求高的核心服務(wù)和可靠性要求低的非核心服務(wù)拆分開(kāi)來(lái),然后重點(diǎn)保證核心服務(wù)的高可用。

避免由于非核心服務(wù)故障,而影響核心服務(wù)。

4、業(yè)務(wù)性能

基于業(yè)務(wù)性能拆分,考慮的是將性能壓力大的模塊拆分出來(lái)。對(duì)于這一點(diǎn),筆者有兩點(diǎn)想法:

  • 避免性能壓力大的服務(wù)影響其他服務(wù)。
  • 將高流量的業(yè)務(wù)獨(dú)立出來(lái),扛不住的情況下,方便水平擴(kuò)展。

比如,在筆者參與的一個(gè)系統(tǒng)中,曾通過(guò) RocketMQ 對(duì)接來(lái)自多家廠商的大量數(shù)據(jù)。

當(dāng)時(shí),就獨(dú)立出來(lái)一個(gè)消息服務(wù),專門(mén)用來(lái)消費(fèi)消息。然后有的是在本地處理,有的通過(guò) RPC 接口轉(zhuǎn)發(fā)到其他服務(wù)處理,有的直接通過(guò) WebSocket 推送到前端展示。

這樣的話,即便流量激增,考慮給消息服務(wù)增加機(jī)器,提高消費(fèi)能力就好了。

當(dāng)了解到上面這幾種拆分方式之后,我們就可以根據(jù)自己的業(yè)務(wù)范圍和技術(shù)團(tuán)隊(duì)規(guī)模,來(lái)考慮自己系統(tǒng)的服務(wù)拆分了。

不過(guò)在這里,尤為值得注意的是,微服務(wù)切忌拆分的過(guò)細(xì),一定要結(jié)合業(yè)務(wù)規(guī)模和技術(shù)團(tuán)隊(duì)的規(guī)模。

筆者以前就遇到一個(gè)項(xiàng)目,在做服務(wù)化的過(guò)程中,當(dāng)時(shí)的技術(shù)負(fù)責(zé)人按照業(yè)務(wù)獨(dú)立性來(lái)拆分服務(wù),結(jié)果拆出了10來(lái)個(gè)服務(wù);然后更狠的是,每個(gè)業(yè)務(wù)服務(wù)又把Service層和Controller層拆分開(kāi)來(lái),每個(gè)業(yè)務(wù)方法都需要遠(yuǎn)程調(diào)用。據(jù)當(dāng)事人描述,這樣做是為了方便后期提升擴(kuò)展能力。

不過(guò)說(shuō)實(shí)話,有些系統(tǒng)業(yè)務(wù)量并沒(méi)有那么大,一味的迎合微服務(wù)中的微字,無(wú)疑是給自己增加難度,破壞整體系統(tǒng)的穩(wěn)定性。所以,在重新梳理了業(yè)務(wù)流程后,筆者對(duì)這個(gè)系統(tǒng)進(jìn)行了重構(gòu),縮減服務(wù)數(shù)量。

在這里,筆者想說(shuō)明一點(diǎn)。Service層和Controller層是可以拆分成多個(gè)模塊的,這個(gè)沒(méi)關(guān)系。不過(guò),它只應(yīng)該是模塊的分離,而不是服務(wù)的拆分。比如我們可以在開(kāi)發(fā)階段把它們拆分成多個(gè)模塊,然后通過(guò) Maven modules 聚合到一塊,在部署運(yùn)行階段,它們還都是一個(gè)服務(wù)。

二、技術(shù)選型

當(dāng)完成了服務(wù)拆分之后,選用什么框架進(jìn)行開(kāi)發(fā)呢,應(yīng)該選擇 Dubbo 還是 SpringCloud ?

筆者不想單純討論它們的優(yōu)劣之處,在這里可以就著這個(gè)問(wèn)題分享下筆者的心路歷程。

最初進(jìn)行選型時(shí),筆者選擇了 SpringCloud,畢竟它號(hào)稱是微服務(wù)一站式解決方案。然后就搭建框架,集成各種組件,完成了一些 demo 的開(kāi)發(fā)和測(cè)試。

不過(guò),在完成這部分工作后,重新審視整個(gè)系統(tǒng),看到SpringCloud中,涉及到的Eureka、Ribbon、Feign、Hystrix、Zuul、Config這些組件。

這時(shí)候,我會(huì)產(chǎn)生兩個(gè)疑問(wèn):

  • 這些組件是不是非用不可 ? 有沒(méi)有更輕便的系統(tǒng)方案 ?
  • 這么多東西,任一環(huán)節(jié)出了問(wèn)題,我們是否能hold的?。?/li>

筆者對(duì)于一站式這個(gè)詞有兩層理解。第一,它簡(jiǎn)化了分布式系統(tǒng)基礎(chǔ)設(shè)施的開(kāi)發(fā),易于開(kāi)發(fā);第二,簡(jiǎn)化的同時(shí),它一定是屏蔽了復(fù)雜的配置和實(shí)現(xiàn)原理,不易于深入理解它的原理。

作為架構(gòu)師或者團(tuán)隊(duì)技術(shù)負(fù)責(zé)人,我們必須對(duì)自己系統(tǒng)涉及到的技術(shù)點(diǎn)做到知根知底,或者說(shuō),最起碼要懂得它們的原理。這樣,即便遇到了問(wèn)題,在 Baidu / Google 不出來(lái)結(jié)果時(shí),也不會(huì)慌。

基于這樣一個(gè)思路,筆者把目光又轉(zhuǎn)向了Dubbo。對(duì)于筆者來(lái)說(shuō),對(duì)Dubbo就比較熟悉了,從它的框架本身來(lái)說(shuō),已經(jīng)實(shí)現(xiàn)了負(fù)載均衡和集群容錯(cuò),調(diào)用方式也是基于接口的。相較SpringCloud而言,不需要再額外引入像Ribbon/Feign這樣的組件。

還有一點(diǎn),Dubbo得益于強(qiáng)大的SPI機(jī)制,我們可以非常方便的擴(kuò)展它。如果業(yè)務(wù)上有需要,在很多地方都可以對(duì)它進(jìn)行擴(kuò)展,比如 RPC 協(xié)議、集群、注冊(cè)中心、序列化方式、線程池等。

不過(guò)話說(shuō)回來(lái),Dubbo只是一個(gè)高性能的 RPC 框架,拿它和SpringCloud相比,更多的還是比較REST和RPC。不過(guò)關(guān)于這一點(diǎn),就一般的項(xiàng)目而已,這點(diǎn)性能差異還不足以一錘定音,最多只是錦上添花而已。

至于其他的組件,比如Hystrix/Zuul/Config/zipkin等,筆者的觀點(diǎn),還是看業(yè)務(wù)規(guī)模。微服務(wù)只是一種設(shè)計(jì)思想,架構(gòu)理念,而不是說(shuō)用到了很多分布式框架才叫微服務(wù)。這些框架的產(chǎn)生,只是為了解決微服務(wù)系統(tǒng)遇到的問(wèn)題的。

需知一口吃不成個(gè)胖子,在做技術(shù)選型時(shí),切記直接對(duì)標(biāo)像阿里、京東、美團(tuán)這樣的大廠經(jīng)驗(yàn),一來(lái),也許我們遇不到那樣的業(yè)務(wù)場(chǎng)景;再者,一般公司也沒(méi)有人家那樣的人才儲(chǔ)備。畢竟如果線上出了問(wèn)題,是沒(méi)人跟你分享?yè)p失的。

總的來(lái)說(shuō),還是結(jié)合自己的實(shí)際情況,以最穩(wěn)妥的技術(shù)方案,完成業(yè)務(wù)上的需求。

三、節(jié)外生枝

拆分了服務(wù),也完成了技術(shù)方案的選型,那就萬(wàn)事大吉,開(kāi)始擼代碼了嗎 ? 如果單純作為開(kāi)發(fā)人員,那確實(shí)開(kāi)擼就行了。如果你是一個(gè)系統(tǒng)的負(fù)責(zé)人,只滿足于高屋建瓴,不考慮細(xì)節(jié)問(wèn)題,那必然會(huì)節(jié)外生枝。

1、超時(shí)和容錯(cuò)

服務(wù)化之后,不同服務(wù)之間的調(diào)用就是遠(yuǎn)程調(diào)用。遠(yuǎn)程調(diào)用有個(gè)最基本的設(shè)置,即超時(shí)時(shí)間。

比如,在Dubbo中,默認(rèn)的超時(shí)時(shí)間是1秒。我們不能單純的使用默認(rèn)值或者統(tǒng)一設(shè)置成另外的值,為Dubbo設(shè)置超時(shí)時(shí)間最好是有針對(duì)性的。

比如,比較簡(jiǎn)單的業(yè)務(wù)可以設(shè)置的短一些;但對(duì)于復(fù)雜業(yè)務(wù)而言,則需要適當(dāng)?shù)募娱L(zhǎng)這個(gè)時(shí)間。因?yàn)?,這里還涉及到一個(gè)集群容錯(cuò)的問(wèn)題。

Dubbo中,集群容錯(cuò)的默認(rèn)策略是失敗重試,次數(shù)為2。假如有的業(yè)務(wù)本身就需要耗費(fèi)較長(zhǎng)的時(shí)間來(lái)執(zhí)行,因?yàn)槌瑫r(shí)時(shí)間太短就會(huì)觸發(fā)容錯(cuò)機(jī)制,來(lái)重試。大量的并發(fā)重試請(qǐng)求,很可能會(huì)占滿Dubbo的線程池,甚至影響后端數(shù)據(jù)庫(kù)系統(tǒng),導(dǎo)致連接被耗盡。

2、容錯(cuò)和冪等性

我們上面說(shuō),如果超時(shí)時(shí)間設(shè)置太短,有可能會(huì)導(dǎo)致大量請(qǐng)求會(huì)不斷重試,而導(dǎo)致異常。

這里還隱瞞著另外一個(gè)細(xì)節(jié),即讀請(qǐng)求和寫(xiě)請(qǐng)求。如果是讀請(qǐng)求,那么重試無(wú)所謂;如果是寫(xiě)請(qǐng)求,我們的接口是否也支持自動(dòng)重試呢 ? 這就會(huì)涉及到接口冪等性問(wèn)題。

如果寫(xiě)請(qǐng)求的接口,不支持冪等性,那么集群容錯(cuò)就得改為 failfast,即快速失敗。

3、分布式事務(wù)

筆者感覺(jué),分布式事務(wù)在業(yè)界是一個(gè)沒(méi)有徹底解決的技術(shù)難題。 沒(méi)有通用的解決方案,也沒(méi)有既高效又簡(jiǎn)便的手段。

雖然如此,但我們也得事先考慮到這一點(diǎn),不然數(shù)據(jù)肯定會(huì)變成臟亂差。

在考慮解決方案之前,我們需要先看看自己的系統(tǒng)是不是真的追求強(qiáng)一致性;按照BASE理論,在分布式系統(tǒng)中,允許不同節(jié)點(diǎn)在同步的過(guò)程存在延時(shí),但是經(jīng)過(guò)一段時(shí)間的修復(fù)后,能夠達(dá)到數(shù)據(jù)的最終一致性。

基于這兩個(gè)思路,我們才好制定自己的分布式事務(wù)方案。

對(duì)于要求強(qiáng)一致性的場(chǎng)景,或許可以考慮XA協(xié)議,通過(guò)二階段提交或者三階段提交來(lái)保證。

對(duì)于要求最終一致性的場(chǎng)景,可以考慮采用 TCC 模式,補(bǔ)償模式,或者基于消息隊(duì)列的模式。

比如基于消息隊(duì)列模式,可以采用 RocketMQ。它支持事務(wù)消息,那么這時(shí)候整個(gè)流程大概是這樣的:

  • 通過(guò) RocketMQ 發(fā)送事務(wù)消息到消息隊(duì)列;
  • 消息發(fā)送成功,則執(zhí)行本地事務(wù);
  • 如果本地事務(wù)執(zhí)行成功,則提交RocketMQ事務(wù)消息,提交后對(duì)消費(fèi)者可見(jiàn);
  • 如果本地事務(wù)執(zhí)行失敗,則刪除RocketMQ事務(wù)消息,消費(fèi)者不會(huì)看到這條消息。

另外,在這里安利下阿里開(kāi)源的Seata。目前最新版本是1.1.0,支持多種事務(wù)模式,比如 AT、TCC、SAGA 和 XA 事務(wù)模式。

筆者有篇文章是基于 Seata 0.7版本的寫(xiě)的,有興趣的朋友可以了解下。

SpringBoot+Dubbo+Seata分布式事務(wù)實(shí)戰(zhàn)

4、消息隊(duì)列

在分布式系統(tǒng)架構(gòu)中,為了系統(tǒng)間的解耦和異步處理,應(yīng)對(duì)高并發(fā)和大流量,消息隊(duì)列絕對(duì)是一大利器。

在使用這一利器前,我們也得考慮下有可能因?yàn)橄㈥?duì)列帶來(lái)的煩惱。

首先需要考慮的就是可用性,如果消息隊(duì)列不可用,會(huì)不會(huì)對(duì)系統(tǒng)本身造成大量的不可用;

然后,消息會(huì)不會(huì)丟失呢 ? 如何保證消息可靠性傳輸呢?比如要考慮消息隊(duì)列本身的刷盤(pán)機(jī)制、同步機(jī)制;數(shù)據(jù)發(fā)送時(shí)的確認(rèn)和消費(fèi)后的提交;

然后就是重復(fù)消費(fèi),如果保證了消息不會(huì)丟失,多多少少都可能會(huì)有重復(fù)消息的問(wèn)題,這時(shí)候就要考慮重復(fù)消費(fèi)有沒(méi)有問(wèn)題,即消息冪等性;

還有,消息順序性問(wèn)題,你們的業(yè)務(wù)場(chǎng)景里,是否有消息順序性問(wèn)題,如果有這個(gè)問(wèn)題,要么在設(shè)計(jì)時(shí)規(guī)避它,要么在消費(fèi)時(shí)保證它的順序。

5、統(tǒng)一日志

隨著微服務(wù)的拆分,日志系統(tǒng)也可能會(huì)演變?yōu)楠?dú)立的模塊。為了查看日志,我們可能需要登錄到不同的服務(wù)器去一個(gè)個(gè)查看。

因此,搭建統(tǒng)一的日志處理平臺(tái)是必然的。我們可以采用 ELK 的解決方案進(jìn)行日志聚合。

在這里,還需要鏈路追蹤問(wèn)題。在微服務(wù)復(fù)雜的鏈?zhǔn)秸{(diào)用中,會(huì)比單體應(yīng)用更難以定位和追蹤問(wèn)題。

對(duì)于這個(gè)問(wèn)題,我們考慮引入分布式調(diào)用鏈,生成一個(gè)全局唯一的 TraceID ,通過(guò)它把整個(gè)調(diào)用鏈串聯(lián)起來(lái)。結(jié)合Dubbo框架的話,我們實(shí)現(xiàn)自己的Filter,用來(lái)透?jìng)鬟@個(gè)TraceID。

具體思路可以參考:SpringBoot+Dubbo集成ELK實(shí)戰(zhàn)

當(dāng)然,我們也可以選用一些成熟的開(kāi)源框架來(lái)解決。

總結(jié)

本文簡(jiǎn)單總結(jié)了微服務(wù)設(shè)計(jì)和開(kāi)發(fā)過(guò)程中,可能會(huì)涉及到的一些問(wèn)題。

以上觀點(diǎn)只是一家之言,僅僅是筆者在過(guò)去時(shí)間里的經(jīng)驗(yàn)總結(jié)。

如果對(duì)您有幫助,請(qǐng)點(diǎn)贊鼓勵(lì)~ 如果您有不同觀點(diǎn),請(qǐng)積極發(fā)言,共同交流~

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

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

  • 突然意識(shí)到我是很幼稚的,本來(lái)在相應(yīng)年紀(jì)應(yīng)該想的事沒(méi)有想、應(yīng)該做的事沒(méi)有做,而是想了一些幼稚的事、做了一些幼稚的事。...
    liuliuliu2016閱讀 173評(píng)論 0 0
  • 在這個(gè)被心靈雞湯充斥年代,勵(lì)志成了活著必須有的姿態(tài),似乎你不努力都對(duì)不起每天升起的太陽(yáng)。 可是,...
    曉莉看世界閱讀 1,815評(píng)論 0 6
  • 在80年前的今天,1937年7月7日,盧溝橋事變?!凹摇本褪亲约旱拿?,“國(guó)”就是自己的根。愛(ài)國(guó)就是愛(ài)家,就是保命根...
    呂明超閱讀 268評(píng)論 0 0

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