【編者的話】隨着架構設計的發展,微服務架構能夠說是目前架構領域煊赫一時的設計理念。在公司,筆者也一直在負責系統的服務化設計和開發工做。前端
今天就來談談微服務落地實踐中的一些問題。但願對微服務設計無從下手的朋友,起到一些參考做用;另外也但願把本身的觀點分享出來,期待與你們一塊兒交流,可以認識到不足之處。
服務拆分
在落地微服務以前,咱們遇到的第一個問題就是:應該如何拆分服務?
你們知道,關於如何拆分服務,並無一個徹底通用的法則。不過有如下幾點要素,咱們能夠參考。
業務獨立性
通常狀況下,咱們第一時間都會考慮到這點。將系統中的業務模塊,按照職責標識出來,每一個單獨的模塊拆分紅一個獨立的服務。數據庫
這樣拆分以後,咱們的服務要知足一個原則:高內聚,低耦合。後端
好比,咱們把一個系統拆分爲商品服務、訂單服務、物流服務。通常狀況下,咱們修改物流服務的時候,並不會影響商品服務,這就是低耦合的體現;那麼訂單服務裏面的功能和邏輯,都是圍繞着訂單這個核心業務流程的,就能夠說它是高內聚的。
業務穩定性
咱們能夠把系統中的業務按照穩定性進行區分。好比,用戶註冊、登陸部分,在我司的系統中,這一塊的代碼只要寫完,基本就再也不會發生變更,那麼我就將他們拆爲用戶服務。服務器
同理,還有日誌服務、監控服務,這些模塊基本上也都是很穩定的業務,能夠考慮單獨拆分。
業務可靠性
這裏講究的是,將可靠性要求高的核心服務和可靠性要求低的非核心服務拆分開來,而後重點保證核心服務的高可用。架構
避免因爲非核心服務故障,而影響核心服務。
業務性能
基於業務性能拆分,考慮的是將性能壓力大的模塊拆分出來。對於這一點,筆者有兩點想法:
避免性能壓力大的服務影響其餘服務。
將高流量的業務獨立出來,扛不住的狀況下,方便水平擴展。併發
好比,在筆者參與的一個系統中,曾經過 RocketMQ 對接來自多家廠商的大量數據。負載均衡
當時,就獨立出來一個消息服務,專門用來消費消息。而後有的是在本地處理,有的經過 RPC 接口轉發到其餘服務處理,有的直接經過 WebSocket 推送到前端展現。框架
這樣的話,即使流量激增,考慮給消息服務增長機器,提升消費能力就行了。異步
當了解到上面這幾種拆分方式以後,咱們就能夠根據本身的業務範圍和技術團隊規模,來考慮本身系統的服務拆分了。分佈式
不過在這裏,尤其值得注意的是,微服務切忌拆分的過細,必定要結合業務規模和技術團隊的規模。
筆者之前就遇到一個項目,在作服務化的過程當中,當時的技術負責人按照業務獨立性來拆分服務,結果拆出了10 來個服務;而後更狠的是,每一個業務服務又把 Service 層和 Controller 層拆分開來,每一個業務方法都須要遠程調用。據當事人描述,這樣作是爲了方便後期提高擴展能力。
不過說實話,有些系統業務量並無那麼大,一味的迎合微服務中的微字,無疑是給本身增長難度,破壞總體系統的穩定性。因此,在從新梳理了業務流程後,筆者對這個系統進行了重構,縮減服務數量。
在這裏,筆者想說明一點。Service 層和 Controller 層是能夠拆分紅多個模塊的,這個不要緊。不過,它只應該是模塊的分離,而不是服務的拆分。好比咱們能夠在開發階段把它們拆分紅多個模塊,而後經過 Maven modules 聚合到一塊,在部署運行階段,它們還都是一個服務。
技術選型
當完成了服務拆分以後,選用什麼框架進行開發呢,應該選擇 Dubbo 仍是 SpringCloud?
筆者不想單純討論它們的優劣之處,在這裏能夠就着這個問題分享下筆者的心路歷程。
最初進行選型時,筆者選擇了 SpringCloud,畢竟它號稱是微服務一站式解決方案。而後就搭建框架,集成各類組件,完成了一些 demo 的開發和測試。
不過,在完成這部分工做後,從新審視整個系統,看到 Spring Cloud 中,涉及到的 Eureka、Ribbon、Feign、Hystrix、Zuul、Config 這些組件。
這時候,我會產生兩個疑問:
這些組件是否是非用不可 ? 有沒有更輕便的系統方案 ?
這麼多東西,任一環節出了問題,咱們是否能hold的住?
筆者對於一站式這個詞有兩層理解。第一,它簡化了分佈式系統基礎設施的開發,易於開發;第二,簡化的同時,它必定是屏蔽了複雜的配置和實現原理,不易於深刻理解它的原理。
做爲架構師或者團隊技術負責人,咱們必須對本身系統涉及到的技術點作到知根知底,或者說,最起碼要懂得它們的原理。這樣,即使遇到了問題,在 Baidu / Google 不出來結果時,也不會慌。
基於這樣一個思路,筆者把目光又轉向了 Dubbo。對於筆者來講,對 Dubbo 就比較熟悉了,從它的框架自己來講,已經實現了負載均衡和集羣容錯,調用方式也是基於接口的。相較 Spring Cloud 而言,不須要再額外引入像 Ribbon/Feign 這樣的組件。
還有一點,Dubbo 得益於強大的 SPI 機制,咱們能夠很是方便的擴展它。若是業務上有須要,在不少地方均可以對它進行擴展,好比 RPC 協議、集羣、註冊中心、序列化方式、線程池等。
不過話說回來,Dubbo 只是一個高性能的 RPC 框架,拿它和 Spring Cloud 相比,更多的仍是比較 REST 和 RPC。不過關於這一點,就通常的項目而已,這點性能差別還不足以一槌定音,最多隻是錦上添花而已。
至於其餘的組件,好比 Hystrix/Zuul/Config/Zipkin 等,筆者的觀點,仍是看業務規模。微服務只是一種設計思想,架構理念,而不是說用到了不少分佈式框架才叫微服務。這些框架的產生,只是爲了解決微服務系統遇到的問題的。
需知一口吃不成個胖子,在作技術選型時,切忌直接對標像阿里、京東、美團這樣的大廠經驗,一來,也許咱們遇不到那樣的業務場景;再者,通常公司也沒有人家那樣的人才儲備。畢竟若是線上出了問題,是沒人跟你分享損失的。
總的來講,仍是結合本身的實際狀況,以最穩妥的技術方案,完成業務上的需求。
節外生枝
拆分了服務,也完成了技術方案的選型,那就萬事大吉,開始擼代碼了嗎 ? 若是單純做爲開發人員,那確實開擼就好了。若是你是一個系統的負責人,只知足於高屋建瓴,不考慮細節問題,那必然會節外生枝。
超時和容錯
服務化以後,不一樣服務之間的調用就是遠程調用。遠程調用有個最基本的設置,即超時時間。
好比,在 Dubbo 中,默認的超時時間是 1 秒。咱們不能單純的使用默認值或者統一設置成另外的值,爲 Dubbo 設置超時時間最好是有針對性的。
好比,比較簡單的業務能夠設置的短一些;但對於複雜業務而言,則須要適當的加長這個時間。由於,這裏還涉及到一個集羣容錯的問題。
在 Dubbo 中,集羣容錯的默認策略是失敗重試,次數爲 2。假若有的業務自己就須要耗費較長的時間來執行,由於超時時間過短就會觸發容錯機制,來重試。大量的併發重試請求,極可能會佔滿 Dubbo 的線程池,甚至影響後端數據庫系統,致使鏈接被耗盡。
容錯和冪等性
咱們上面說,若是超時時間設置過短,有可能會致使大量請求會不斷重試,而致使異常。
這裏還隱瞞着另一個細節,即讀請求和寫請求。若是是讀請求,那麼重試無所謂;若是是寫請求,咱們的接口是否也支持自動重試呢 ? 這就會涉及到接口冪等性問題。
若是寫請求的接口,不支持冪等性,那麼集羣容錯就得改成 failfast,即快速失敗。
分佈式事務
筆者感受,分佈式事務在業界是一個沒有完全解決的技術難題。 沒有通用的解決方案,也沒有既高效又簡便的手段。
雖然如此,但咱們也得事先考慮到這一點,否則數據確定會變成髒亂差。
在考慮解決方案以前,咱們須要先看看本身的系統是否是真的追求強一致性;按照 BASE 理論,在分佈式系統中,容許不一樣節點在同步的過程存在延時,可是通過一段時間的修復後,可以達到數據的最終一致性。
基於這兩個思路,咱們纔好制定本身的分佈式事務方案。
對於要求強一致性的場景,或許能夠考慮XA協議,經過二階段提交或者三階段提交來保證。
對於要求最終一致性的場景,能夠考慮採用 TCC 模式,補償模式,或者基於消息隊列的模式。
好比基於消息隊列模式,能夠採用 RocketMQ。它支持事務消息,那麼這時候整個流程大概是這樣的:
經過 RocketMQ 發送事務消息到消息隊列;
消息發送成功,則執行本地事務;
若是本地事務執行成功,則提交 RocketMQ 事務消息,提交後對消費者可見;
若是本地事務執行失敗,則刪除 RocketMQ 事務消息,消費者不會看到這條消息。
另外,在這裏安利下阿里開源的 Seata。目前最新版本是 1.1.0,支持多種事務模式,好比 AT、TCC、SAGA 和 XA 事務模式。
消息隊列
在分佈式系統架構中,爲了系統間的解耦和異步處理,應對高併發和大流量,消息隊列絕對是一大利器。
在使用這一利器前,咱們也得考慮下有可能由於消息隊列帶來的煩惱。
首先須要考慮的就是可用性,若是消息隊列不可用,會不會對系統自己形成大量的不可用;
而後,消息會不會丟失呢 ? 如何保證消息可靠性傳輸呢?好比要考慮消息隊列自己的刷盤機制、同步機制;數據發送時的確認和消費後的提交;
而後就是重複消費,若是保證了消息不會丟失,多多少少均可能會有重複消息的問題,這時候就要考慮重複消費有沒有問題,即消息冪等性;
還有,消息順序性問題,大家的業務場景裏,是否有消息順序性問題,若是有這個問題,要麼在設計時規避它,要麼在消費時保證它的順序。
統一日誌
隨着微服務的拆分,日誌系統也可能會演變爲獨立的模塊。爲了查看日誌,咱們可能須要登陸到不一樣的服務器去一個個查看。
所以,搭建統一的日誌處理平臺是必然的。咱們能夠採用 ELK 的解決方案進行日誌聚合。
在這裏,還須要鏈路追蹤問題。在微服務複雜的鏈式調用中,會比單體應用更難以定位和追蹤問題。
對於這個問題,咱們考慮引入分佈式調用鏈,生成一個全局惟一的 TraceID ,經過它把整個調用鏈串聯起來。結合 Dubbo 框架的話,咱們實現本身的 Filter,用來透傳這個 TraceID。
固然,咱們也能夠選用一些成熟的開源框架來解決。
總結
本文簡單總結了微服務設計和開發過程當中,可能會涉及到的一些問題。以上觀點只是一家之言,僅僅是筆者在過去時間裏的經驗總結。