title: 微服務(wù)總結(jié)
date: 2020/02/20 10:33
author: yujx
前言:系統(tǒng)架構(gòu)演變
注:微服務(wù)架構(gòu)之前都是前后端未分離的時代。
單體應(yīng)用架構(gòu)
當(dāng)網(wǎng)站流量很小時彪杉,只需一個應(yīng)用花盐,將所有功能都部署在一起冠蒋,以減少部署節(jié)點(diǎn)和成本识脆。
比如說一個電商系統(tǒng)设江,里面會包含很多用戶管理暇屋,商品管理似袁,訂單管理,物流管理等等很多模塊咐刨,我們會把它們做成一個web項(xiàng)目昙衅,然后部署到一臺tomcat服務(wù)器上。
優(yōu)點(diǎn):
- 項(xiàng)目架構(gòu)簡單定鸟,小型項(xiàng)目的話鸳碧,開發(fā)成本低
- 項(xiàng)目部署在一個Tomcat上怀喉,易于部署
缺點(diǎn):
- 全部功能集成在一個工程中熏迹,對于大型項(xiàng)目來講不易開發(fā)和維護(hù)
- 項(xiàng)目模塊之間緊密耦合率翅,單點(diǎn)容錯率低,并發(fā)能力差(一個功能掛掉沸久,其它全部歇菜季眷。單個Tomcat的并發(fā)有限制)
- 無法針對不同模塊進(jìn)行針對性優(yōu)化和水平擴(kuò)展
垂直應(yīng)用架構(gòu)
當(dāng)訪問量逐漸增大,單一應(yīng)用無法滿足需求卷胯,單一應(yīng)用只能依靠增加節(jié)點(diǎn)來應(yīng)對子刮,但是這時候會發(fā)現(xiàn)并不是所有的模塊都會有比較大的訪問量。
還是以上面的電商為例子窑睁,用戶訪問量的增加可能影響的只是用戶和訂單模塊挺峡,但是對消息模塊的影響就比較小. 那么此時我們希望只多增加幾個訂單模塊,而不增加消息模塊担钮。此時單體應(yīng)用就做不到了橱赠,垂直應(yīng)用就應(yīng)運(yùn)而生了。
垂直應(yīng)用架構(gòu)箫津,就是將原來的一個應(yīng)用拆成互不相干的幾個應(yīng)用狭姨,以提升效率。
比如我們可 以將上面電商的單體應(yīng)用拆分成:
- 電商系統(tǒng)(商品管理苏遥、訂單管理)
- 后臺系統(tǒng)(用戶管理送挑、訂單管理、客戶管理)
- CMS系統(tǒng)(廣告管理暖眼、營銷管理)
這樣拆分完畢之后,一旦用戶訪問量變大纺裁,只需要增加電商系統(tǒng)的節(jié)點(diǎn)就可以了诫肠,而無需增加后臺和CMS的節(jié)點(diǎn)司澎。
優(yōu)點(diǎn):
- 系統(tǒng)拆分實(shí)現(xiàn)了流量分擔(dān),解決了并發(fā)問題
- 可以針對不同模塊進(jìn)行優(yōu)化和水平擴(kuò)展(集群)
- 一個系統(tǒng)的問題不會影響到其他系統(tǒng)蛤铜,提高容錯率
缺點(diǎn):系統(tǒng)間相互獨(dú)立,無法互相調(diào)用,從而導(dǎo)致會有很多重復(fù)開發(fā)工作
分布式架構(gòu)
當(dāng)垂直應(yīng)用越來越多诚些,重復(fù)的業(yè)務(wù)代碼就會越來越多椅您。這時候韭邓,我們就思考可不可以將重復(fù)的代碼抽取出來,做成統(tǒng)一的業(yè)務(wù)層作為獨(dú)立的服務(wù),然后由前端控制層調(diào)用不同的業(yè)務(wù)層服務(wù)呢?
這就產(chǎn)生了新的分布式系統(tǒng)架構(gòu)。它將把工程拆分成表現(xiàn)層和服務(wù)層兩個部分件甥,服務(wù)層中包含業(yè)務(wù) 邏輯捌议。表現(xiàn)層只需要處理和頁面的交互,業(yè)務(wù)邏輯都是調(diào)用服務(wù)層的服務(wù)來實(shí)現(xiàn)引有。
優(yōu)點(diǎn):抽取公共的功能為服務(wù)層瓣颅,提高代碼復(fù)用性
缺點(diǎn):系統(tǒng)間耦合度變高,調(diào)用關(guān)系錯綜復(fù)雜譬正,難以維護(hù)
注:由于當(dāng)時前后端還未分離宫补,所以一定要有一個web層進(jìn)行前端代碼的書寫。(我認(rèn)為的曾我,不一定對)
SOA架構(gòu)(面向服務(wù)架構(gòu))
在分布式架構(gòu)下粉怕,當(dāng)服務(wù)越來越多,容量的評估抒巢,小服務(wù)資源的浪費(fèi)等問題逐漸顯現(xiàn)贫贝,此時需增加一個調(diào)度中心對集群進(jìn)行實(shí)時管理。此時蛉谜,用于資源調(diào)度和治理中心是關(guān)鍵稚晚。
注1:資源就是指的我們的服務(wù)凤优。
注2:面向服務(wù)架構(gòu)引出了一個概念 —— 服務(wù)治理(SOA governance)
SOA是一種粗粒度、松耦合服務(wù)架構(gòu)蜈彼,服務(wù)之間通過簡單、精確定義接口進(jìn)行通訊俺驶,不涉及底層編程接口和通訊模型幸逆。SOA可以看作是B/S模型、Web Service技術(shù)之后的自然延伸暮现。
服務(wù)治理还绘,也稱為SOA治理,是指用來管理SOA的采用和實(shí)現(xiàn)的過程栖袋。以下是在2006年時IBM對于服務(wù)治理要點(diǎn)的總結(jié):
- 服務(wù)定義(服務(wù)的范圍拍顷、接口和邊界)
- 服務(wù)部署生命周期(各個生命周期階段)
- 服務(wù)版本治理(包括兼容性)
- 服務(wù)遷移(啟用和退役)
- 服務(wù)注冊中心(依賴關(guān)系)
- 服務(wù)消息模型(規(guī)范數(shù)據(jù)模型)
- 服務(wù)監(jiān)視(進(jìn)行問題確定)
- 服務(wù)所有權(quán)(企業(yè)組織)
- 服務(wù)測試(重復(fù)測試)
- 服務(wù)安全(包括可接受的保護(hù)范圍)
限于當(dāng)時的技術(shù)發(fā)展水平,廣大軟件設(shè)計(jì)與開發(fā)人員對于SOA和服務(wù)治理的技術(shù)認(rèn)知還主要停留在Web Service和ESB總線等技術(shù)和規(guī)范上塘幅,并沒有真正在軟件開發(fā)中得以充分落地昔案。
中心化:服務(wù)之間互相調(diào)用通過ESB實(shí)現(xiàn),這樣的缺點(diǎn)电媳,網(wǎng)上寫了很多踏揣,這里不贅述了。
去中心化:服務(wù)注冊到注冊中心捞稿,當(dāng)調(diào)用時,服務(wù)調(diào)用時拼缝,調(diào)用方通過注冊中心獲取要調(diào)用的地址娱局,進(jìn)行調(diào)用(當(dāng)然,還可以客戶端緩存下來)
優(yōu)點(diǎn):使用注冊中心\ESB解決了服務(wù)間調(diào)用關(guān)系的自動調(diào)節(jié)
缺點(diǎn):
- 服務(wù)間會有依賴關(guān)系咧七,一旦某個環(huán)節(jié)出錯會影響較大(服務(wù)雪崩)
- 服務(wù)關(guān)系復(fù)雜衰齐,運(yùn)維、測試部署困難
微服務(wù)
微服務(wù)是由以單一應(yīng)用程序構(gòu)成的小服務(wù)猪叙,自己擁有自己的行程與輕量化處理娇斩,服務(wù)依業(yè)務(wù)功能設(shè)計(jì),以全自動的方式部署穴翩,與其他服務(wù)使用 HTTP API 通信犬第。同時服務(wù)會使用最小的規(guī)模的集中管理 (例如 Docker) 能力,服務(wù)可以用不同的編程語言與數(shù)據(jù)庫等組件實(shí)現(xiàn)芒帕。(微服務(wù)使用不同的數(shù)據(jù)庫) —— WIKI
微服務(wù)實(shí)際上是SOA的一個子集歉嗓,微服務(wù)架構(gòu)強(qiáng)調(diào)的一個重點(diǎn)是“業(yè)務(wù)需要徹底的組件化和服務(wù)化”,原有的單個業(yè)務(wù)系統(tǒng)會拆分為多個可以獨(dú)立開發(fā)背蟆、設(shè)計(jì)鉴分、運(yùn)行的小應(yīng)用哮幢。這些小應(yīng)用之間通過服務(wù)完成交互和集成。
微服務(wù)的特點(diǎn):
- 單一職責(zé):微服務(wù)中每一個服務(wù)都對應(yīng)唯一的業(yè)務(wù)能力志珍,做到單一職責(zé)
- 微:微服務(wù)的服務(wù)拆分粒度很小橙垢,例如一個用戶管理就可以作為一個服務(wù)。每個服務(wù)雖小伦糯,但“五臟俱全”柜某。
- 面向服務(wù):面向服務(wù)是說每個服務(wù)都要對外暴露服務(wù)接口API。并不關(guān)心服務(wù)的技術(shù)實(shí)現(xiàn)敛纲,做到與平臺和語言無關(guān)喂击,也不限定用什么技術(shù)實(shí)現(xiàn),只要提供Rest的接口即可淤翔。
- 自治:自治是說服務(wù)間互相獨(dú)立翰绊,互不干擾
- 團(tuán)隊(duì)獨(dú)立:每個服務(wù)都是一個獨(dú)立的開發(fā)團(tuán)隊(duì),人數(shù)不能過多旁壮。
- 技術(shù)獨(dú)立:因?yàn)槭敲嫦蚍?wù)监嗜,提供Rest接口,使用什么技術(shù)沒有別人干涉
- 前后端分離:采用前后端分離開發(fā)寡具,提供統(tǒng)一Rest接口秤茅,后端不用再為PC、移動段開發(fā)不同接口
- 數(shù)據(jù)庫分離:每個服務(wù)都使用自己的數(shù)據(jù)源
- 部署獨(dú)立童叠,服務(wù)間雖然有調(diào)用框喳,但要做到服務(wù)重啟不影響其它服務(wù)。有利于持續(xù)集成和持續(xù)交付厦坛。每個服務(wù)都是獨(dú)立的組件五垮,可復(fù)用,可替換杜秸,降低耦合放仗,易維護(hù)
微服務(wù)的設(shè)計(jì)原則
Spring Cloud架構(gòu)格式
微服務(wù)架構(gòu)的常見問題:
- 這么多小服務(wù),如何管理他們撬碟?(服務(wù)注冊與發(fā)現(xiàn) -> 注冊中心[服務(wù)注冊 發(fā)現(xiàn) 剔除])
- 這么多小服務(wù)诞挨,他們之間如何通訊?(rest\rpc)
- 這么多小服務(wù)呢蛤,客戶端怎么訪問他們?(服務(wù)網(wǎng)關(guān))
- 這么多小服務(wù)惶傻,一旦出現(xiàn)問題了,應(yīng)該如何自處理其障?(服務(wù)容錯)
- 這么多小服務(wù)银室,一旦出現(xiàn)問題了,應(yīng)該如何排錯?(鏈路追蹤)
總結(jié)
系統(tǒng)架構(gòu)實(shí)際上是由于現(xiàn)有架構(gòu)滿足不了需求從而對原有架構(gòu)進(jìn)行演變過來的蜈敢,所以在選擇的時候一定要選擇適合的辜荠。
微服務(wù)架構(gòu)是SOA架構(gòu)的子集,只不過微服務(wù)定義了服務(wù)劃分的粒度抓狭、通信方式伯病。
一、微服務(wù)架構(gòu)常見概念
1.1 服務(wù)注冊與發(fā)現(xiàn)
一般會通過注冊中心來實(shí)現(xiàn)服務(wù)的注冊與發(fā)現(xiàn)否过,服務(wù)注冊中心一般有下面3個功能:
- 服務(wù)注冊:服務(wù)實(shí)例將自身服務(wù)信息注冊到注冊中心狱从。
- 服務(wù)發(fā)現(xiàn):服務(wù)實(shí)例通過注冊中心,獲取到注冊到其中的服務(wù)實(shí)例的信息叠纹,通過這些信息去請求它們提供的服務(wù)。
- 服務(wù)剔除:服務(wù)注冊中心將出問題的服務(wù)自動剔除到可用列表之外敞葛,使其不會被調(diào)用到誉察。
1.2 服務(wù)調(diào)用
在微服務(wù)架構(gòu)中,通常存在多個服務(wù)之間的遠(yuǎn)程調(diào)用的需求惹谐。目前主流的遠(yuǎn)程調(diào)用技術(shù)有基于
HTTP的RESTful接口以及基于TCP的RPC協(xié)議持偏。
REST:這是一種HTTP調(diào)用的格式,更標(biāo)準(zhǔn)氨肌,更通用鸿秆,無論哪種語言都支持http協(xié)議
RPC:一種進(jìn)程間通信方式。允許像調(diào)用本地服務(wù)一樣調(diào)用遠(yuǎn)程服務(wù)怎囚。RPC框架的主要目標(biāo)就是讓遠(yuǎn)程服務(wù)調(diào)用更簡單卿叽、透明。RPC框架負(fù)責(zé)屏蔽底層的傳輸方式恳守、序列化方式和通信細(xì)節(jié)考婴。開發(fā)人員在使用的時候只需要了解誰在什么位置提供了什么樣的遠(yuǎn)程服務(wù)接口即可,并不需要關(guān)心底層通信細(xì)節(jié)和調(diào)用過程催烘。
區(qū)別:
REST | RPC | |
---|---|---|
通訊協(xié)議 | HTTP | 一般使用TCP |
性能 | 略低 | 較高 |
靈活度 | 高 | 低 |
應(yīng)用 | 一般應(yīng)用于微服務(wù)架構(gòu) | 一般應(yīng)用于SOA架構(gòu) |
1.3 服務(wù)網(wǎng)關(guān)
隨著微服務(wù)的不斷增多沥阱,不同的微服務(wù)一般會有不同的網(wǎng)絡(luò)地址,而外部客戶端可能需要調(diào)用多個服務(wù)的接口才能完成一個業(yè)務(wù)需求伊群,如果讓客戶端直接與各個微服務(wù)通信可能出現(xiàn):
- 客戶端需要調(diào)用不同的url地址考杉,增加難度
- 在一定的場景下,存在跨域請求的問題
- 每個微服務(wù)都需要進(jìn)行單獨(dú)的身份認(rèn)證
針對這些問題舰始,API網(wǎng)關(guān)順勢而生崇棠。
API網(wǎng)關(guān)直面意思是將所有API調(diào)用統(tǒng)一接入到API網(wǎng)關(guān)層,由網(wǎng)關(guān)層統(tǒng)一接入和輸出蔽午。一個網(wǎng)關(guān)的基本功能有:統(tǒng)一接入易茬、安全防護(hù)、協(xié)議適配、流量管控抽莱、長短鏈接支持范抓、容錯能力。有了網(wǎng)關(guān)之后食铐,各個API服務(wù)提供團(tuán)隊(duì)可以專注于自己的的業(yè)務(wù)邏輯處理匕垫,而API網(wǎng)關(guān)更專注于安全、流量虐呻、路由等問題象泵。
1.4 服務(wù)容錯
在微服務(wù)當(dāng)中,一個請求經(jīng)常會涉及到調(diào)用幾個服務(wù)斟叼,如果其中某個服務(wù)不可用偶惠,沒有做服務(wù)容錯的話,極有可能會造成一連串的服務(wù)不可用朗涩,這就是雪崩效應(yīng)忽孽。
我們沒法預(yù)防雪崩效應(yīng)的發(fā)生,只能盡可能去做好容錯谢床。服務(wù)容錯的三個核心思想是:
- 不被外界環(huán)境影響(觀察服務(wù)的外界環(huán)境兄一,例如服務(wù)器CPU、內(nèi)存识腿,保證外界環(huán)境是良好的出革,不會影響應(yīng)用內(nèi)部)
- 不被上游請求壓垮
- 不被下游響應(yīng)拖垮
1.5 鏈路追蹤
隨著微服務(wù)架構(gòu)的流行,服務(wù)按照不同的維度進(jìn)行拆分渡讼,一次請求往往需要涉及到多個服務(wù)骂束。互聯(lián) 網(wǎng)應(yīng)用構(gòu)建在不同的軟件模塊集上成箫,這些軟件模塊栖雾,有可能是由不同的團(tuán)隊(duì)開發(fā)、可能使用不同的編程 語言來實(shí)現(xiàn)伟众、有可能布在了幾千臺服務(wù)器析藕,橫跨多個不同的數(shù)據(jù)中心。因此凳厢,就需要對一次請求涉及的多個服務(wù)鏈路進(jìn)行日志記錄账胧,性能監(jiān)控即鏈路追蹤。
二先紫、微服務(wù)架構(gòu)的常見解決方案
2.1 ServiceComb
Apache ServiceComb治泥,前身是華為云的微服務(wù)引擎 CSE (Cloud Service Engine) 云服務(wù),是全球首個Apache微服務(wù)頂級項(xiàng)目遮精。它提供了一站式的微服務(wù)開源解決方案居夹,致力于幫助企業(yè)败潦、用戶和開發(fā)者將企業(yè)應(yīng)用輕松微服務(wù)化上云,并實(shí)現(xiàn)對微服務(wù)應(yīng)用的高效運(yùn)維管理准脂。
2.2 SpringCloud
Spring Cloud是一系列框架的集合劫扒。它利用Spring Boot的開發(fā)便利性巧妙地簡化了分布式系統(tǒng)基 礎(chǔ)設(shè)施的開發(fā),如服務(wù)發(fā)現(xiàn)注冊狸膏、配置中心沟饥、消息總線、負(fù)載均衡湾戳、斷路器贤旷、數(shù)據(jù)監(jiān)控等,都可以用 Spring Boot的開發(fā)風(fēng)格做到一鍵啟動和部署砾脑。
Spring Cloud并沒有重復(fù)制造輪子幼驶,它只是將目前各家公司開發(fā)的比較成熟、經(jīng)得起實(shí)際考驗(yàn)的服 務(wù)框架組合起來韧衣,通過Spring Boot風(fēng)格進(jìn)行再封裝屏蔽掉了復(fù)雜的配置和實(shí)現(xiàn)原理县遣,最終給開發(fā)者留 出了一套簡單易懂、易部署和易維護(hù)的分布式系統(tǒng)開發(fā)工具包汹族。
注:上面的組件都是可以任意組合的。
其中SpringCloud Netflix已經(jīng)停止開發(fā)其兴,所以我們只能使用SpringCloud Alibaba了顶瞒。
2.2.1 SpringCloud Alibaba
Spring Cloud Alibaba 致力于提供微服務(wù)開發(fā)的一站式解決方案。此項(xiàng)目包含開發(fā)分布式應(yīng)用微服務(wù)的必需組件元旬,方便開發(fā)者通過 Spring Cloud 編程模型輕松使用這些組件來開發(fā)分布式應(yīng)用服務(wù)榴徐。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置匀归,就可以將 Spring Cloud 應(yīng)用接入阿里微服務(wù)解決方案坑资,通過阿里中間件來迅速搭建分布式應(yīng)用系統(tǒng)。
主要功能
服務(wù)限流降級:默認(rèn)支持 WebServlet穆端、WebFlux, OpenFeign袱贮、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ限流降級功能的接入体啰,可以在運(yùn)行時通過控制臺實(shí)時修改限流降級規(guī)則攒巍,還支持查看限流降級 Metrics 監(jiān)控。
服務(wù)注冊與發(fā)現(xiàn):適配Spring Cloud 服務(wù)注冊與發(fā)現(xiàn)標(biāo)準(zhǔn)荒勇,默認(rèn)集成了 Ribbon 的支持柒莉。
分布式配置管理:支持分布式系統(tǒng)中的外部化配置,配置更改時自動刷新沽翔。 消息驅(qū)動能力:基于 Spring Cloud Stream為微服務(wù)應(yīng)用構(gòu)建消息驅(qū)動能力兢孝。
分布式事務(wù):使用 @GlobalTransactional 注解,高效并且對業(yè)務(wù)零侵入地解決分布式事務(wù)問題。
阿里云對象存儲:阿里云提供的海量跨蟹、安全雳殊、低成本、高可靠的云存儲服務(wù)喷市。支持在任何應(yīng)用相种、任何時間、任何地點(diǎn)存儲和訪問任意類型的數(shù)據(jù)品姓。
分布式任務(wù)調(diào)度:提供秒級寝并、精準(zhǔn)、高可靠腹备、高可用的定時(基于 Cron
表達(dá)式)任務(wù)調(diào)度服務(wù)衬潦。同時提供分布式的任務(wù)執(zhí)行模型,如網(wǎng)格任務(wù)植酥。網(wǎng)格任務(wù)支持海量子任務(wù)均勻分配到所有 Worker(schedulerx-client)上執(zhí)行镀岛。
阿里云短信服務(wù):覆蓋全球的短信服務(wù),友好友驮、高效漂羊、智能的互聯(lián)化通訊能力,幫助企業(yè)迅速搭建客戶觸達(dá)通道卸留。
組件
Sentinel:把流量作為切入點(diǎn)走越,從流量控制、熔斷降級耻瑟、系統(tǒng)負(fù)載保護(hù)等多個維度保護(hù)服務(wù)的穩(wěn)定性旨指。
Nacos:一個更易于構(gòu)建云原生應(yīng)用的動態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺喳整。
RocketMQ:一款開源的分布式消息系統(tǒng)谆构,基于高可用分布式集群技術(shù),提供低延時的框都、高可靠的消息發(fā)布與訂閱服務(wù)搬素。
Dubbo:Apache Dubbo? 是一款高性能 Java RPC 框架。
Seata:阿里巴巴開源產(chǎn)品魏保,一個易于使用的高性能微服務(wù)分布式事務(wù)解決方案摄狱。
Alibaba Cloud ACM:一款在分布式架構(gòu)環(huán)境中對應(yīng)用配置進(jìn)行集中管理和推送的應(yīng)用配置中心產(chǎn)品脆荷。
Alibaba Cloud OSS: 阿里云對象存儲服務(wù)(Object Storage Service,簡稱 OSS),是阿里云提供的海量墓律、安全桅锄、低成本赘阀、高可靠的云存儲服務(wù)。您可以在任何應(yīng)用戒傻、任何時間、任何地點(diǎn)存儲和訪問任意類型的數(shù)據(jù)蜂筹。
Alibaba Cloud SchedulerX: 阿里中間件團(tuán)隊(duì)開發(fā)的一款分布式任務(wù)調(diào)度產(chǎn)品需纳,提供秒級、精準(zhǔn)艺挪、高可靠不翩、高可用的定時(基于 Cron 表達(dá)式)任務(wù)調(diào)度服務(wù)。
Alibaba Cloud SMS: 覆蓋全球的短信服務(wù)麻裳,友好口蝠、高效、智能的互聯(lián)化通訊能力津坑,幫助企業(yè)迅速搭建客戶觸達(dá)通道妙蔗。
三、服務(wù)注冊與發(fā)現(xiàn) Nacos Discovery
3.1 簡介
服務(wù)注冊:在服務(wù)治理框架中疆瑰,都會構(gòu)建一個注冊中心眉反,每個服務(wù)單元向注冊中心登記自己提供服務(wù)的詳細(xì)信息。并在注冊中心形成一張服務(wù)的清單穆役,服務(wù)注冊中心需要以心跳的方式去監(jiān)測清單中的服務(wù)是否可用寸五,如果不可用,需要在服務(wù)清單中剔除不可用的服務(wù)耿币。
服務(wù)發(fā)現(xiàn):服務(wù)調(diào)用方向服務(wù)注冊中心咨詢服務(wù)梳杏,并獲取所有服務(wù)的實(shí)例清單,實(shí)現(xiàn)對具體服務(wù)實(shí)例的訪問掰读。
常見的注冊中心
Zookeeper
zookeeper是一個分布式服務(wù)框架,是Apache Hadoop 的一個子項(xiàng)目叭莫,它主要是用來解決分布式應(yīng)用中經(jīng)常遇到的一些數(shù)據(jù)管理問題蹈集,如:統(tǒng)一命名服務(wù)、狀態(tài)同步服務(wù)雇初、集群管理拢肆、分布式應(yīng)用配置項(xiàng)的管理等。
Eureka
Eureka是Springcloud Netflix中的重要組件靖诗,主要作用就是做服務(wù)注冊和發(fā)現(xiàn)郭怪。
Consul
Consul是基于GO語言開發(fā)的開源工具,主要面向分布式刊橘,服務(wù)化的系統(tǒng)提供服務(wù)注冊鄙才、服務(wù)發(fā)現(xiàn)和配置管理的功能。Consul的功能都很實(shí)用促绵,其中包括:服務(wù)注冊/發(fā)現(xiàn)攒庵、健康檢查嘴纺、Key/Value 存儲、多數(shù)據(jù)中心和分布式一致性保證等特性浓冒。Consul本身只是一個二進(jìn)制的可執(zhí)行文件栽渴,所以安裝和部署都非常簡單,只需要從官網(wǎng)下載后稳懒,在執(zhí)行對應(yīng)的啟動腳本即可闲擦。
Nacos
Nacos是一個更易于構(gòu)建云原生應(yīng)用的動態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺场梆。它是 Spring Cloud Alibaba 組件之一墅冷,負(fù)責(zé)服務(wù)注冊發(fā)現(xiàn)和服務(wù)配置,可以這樣認(rèn)為nacos=eureka+config辙谜。
3.2 Nacos 實(shí)戰(zhàn)
3.2.1 安裝
docker pull nacos/nacos-server
docker run --env MODE=standalone --name nacos -d -p 8848:8848 nacos/nacos-server
瀏覽器訪問 http://localhost:8848/nacos 就可以訪問服務(wù)俺榆,默認(rèn)密碼為nacos/nacos。
3.2.2 將微服務(wù)注冊到nacos
1装哆、在pom.xml中添加依賴
<!--nacos客戶端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2罐脊、在application.yml中添加nacos服務(wù)的地址
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
3、啟動微服務(wù)蜕琴,查看是否注冊到nacos
3.2.3 示例:訂單微服務(wù)調(diào)用商品微服務(wù)
1萍桌、在訂單微服務(wù)的主啟動類上添加@EnableDiscoveryClient
注解
2、將RestTemplate注入容器
@EnableDiscoveryClient
@EntityScan("cn.x5456.common")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
3凌简、創(chuàng)建OrderController
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
// 我們可以通過它獲取到注冊到注冊中心的所有服務(wù)
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private OrderDao orderDao;
//下單
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
log.info("接收到{}號商品的下單請求,接下來調(diào)用商品微服務(wù)查詢此商品信息", pid);
// 通過discoveryClient獲取服務(wù)的實(shí)例信息
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
ServiceInstance instance = instances.get(0);
//調(diào)用商品微服務(wù)上炎,查詢商品信息
Product product = restTemplate.getForObject("http://" + instance.getHost() + ":" + instance.getPort() + "/product/" + pid, Product.class);
log.info("查詢到{}號商品的信息,內(nèi)容是:{}", pid, JSON.toJSONString(product));
//下單(創(chuàng)建訂單)
Order order = new Order();
order.setUid(1);
order.setUsername("測試用戶");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
return order;
}
}
4、啟動訂單和商品微服務(wù)雏搂,訪問 http://127.0.0.1:8091/order/prod/1
注:一般情況下藕施,我們都會將一個微服務(wù)部署好多份,目的就是為了可以對請求進(jìn)行負(fù)載凸郑,像我們上面那樣寫裳食,每次調(diào)用都會使用同一個服務(wù)。
3.2.4 配置服務(wù)調(diào)用采用負(fù)載均衡
負(fù)載均衡就是將負(fù)載(工作任務(wù)芙沥,訪問請求)進(jìn)行分?jǐn)偟蕉鄠€操作單元(服務(wù)器,組件)上進(jìn)行執(zhí)行诲祸。
根據(jù)負(fù)載均衡發(fā)生位置的不同,一般分為服務(wù)端負(fù)載均衡和客戶端負(fù)載均衡而昨。
服務(wù)端負(fù)載均衡指的是發(fā)生在服務(wù)提供者一方救氯,比如常見的nginx負(fù)載均衡。
而客戶端負(fù)載均衡指的是發(fā)生在服務(wù)請求的一方歌憨,也就是在發(fā)送請求之前已經(jīng)選好了由哪個實(shí)例處理請求。
我們在微服務(wù)調(diào)用關(guān)系中一般會選擇客戶端負(fù)載均衡享扔,也就是在服務(wù)調(diào)用的一方來決定服務(wù)由哪個提供者執(zhí)行暮顺。
使用Netflix Ribbon 實(shí)現(xiàn)
1捶码、再啟動一個商品微服務(wù)
2、在注入的RestTemplate上加上@LoadBalanced
注解
@EnableDiscoveryClient // 這個注解可以刪除
@EntityScan("cn.x5456.common")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
@Bean
@LoadBalanced // 添加這個注解
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
3或链、修改order
方法
// 可以刪掉
@Autowired
private DiscoveryClient discoveryClient;
//下單--自定義負(fù)載均衡
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
log.info("接收到{}號商品的下單請求,接下來調(diào)用商品微服務(wù)查詢此商品信息", pid);
Product product = restTemplate.getForObject("http://service-product/product/" + pid, Product.class);
log.info("查詢到{}號商品的信息,內(nèi)容是:{}", pid, JSON.toJSONString(product));
//下單(創(chuàng)建訂單)
Order order = new Order();
order.setUid(1);
order.setUsername("測試用戶");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
log.info("創(chuàng)建訂單成功,訂單信息為{}", JSON.toJSONString(order));
return order;
}
4惫恼、多訪問幾遍 http://127.0.0.1:8091/order/prod/1 ,查看日志澳盐。
Ribbon支持的負(fù)載均衡策略
Ribbon內(nèi)置了多種負(fù)載均衡策略,內(nèi)部負(fù)載均衡的頂級接口為 com.netflix.loadbalancer.IRule祈纯,具體的負(fù)載策略如下表所示:
策略名 | 策略聲明 | 策略描述 | 實(shí)現(xiàn)說明 |
---|---|---|---|
BestAvailableRule | public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule | 選擇一個最小的并發(fā)請求的server | 逐個考察Server,如果Server被tripped了叼耙,則忽略腕窥,在選擇其中ActiveRequestsCount最小的server |
AvailabilityFilteringRule | public class AvailabilityFilteringRule extends PredicateBasedRule | 過濾掉那些因?yàn)橐恢边B接失敗的被標(biāo)記為circuit tripped的后端server,并過濾掉那些高并發(fā)的的后端server(active connections 超過配置的閾值) | 使用一個AvailabilityPredicate來包含過濾server的邏輯筛婉,其實(shí)就就是檢查status里記錄的各個server的運(yùn)行狀態(tài) |
WeightedResponseTimeRule | public class WeightedResponseTimeRule extends RoundRobinRule | 根據(jù)響應(yīng)時間分配一個weight簇爆,響應(yīng)時間越長,weight越小爽撒,被選中的可能性越低入蛆。 | 一個后臺線程定期的從status里面讀取評價(jià)響應(yīng)時間,為每個server計(jì)算一個weight硕勿。Weight的計(jì)算也比較簡單responsetime 減去每個server自己平均的responsetime是server的權(quán)重哨毁。當(dāng)剛開始運(yùn)行,沒有形成status時首尼,使用roubine策略選擇server挑庶。 |
RetryRule | public class RetryRule extends AbstractLoadBalancerRule | 對選定的負(fù)載均衡策略機(jī)上重試機(jī)制言秸。 | 在一個配置時間段內(nèi)當(dāng)選擇server不成功软能,則一直嘗試使用subRule的方式選擇一個可用的server |
RoundRobinRule | public class RoundRobinRule extends AbstractLoadBalancerRule | roundRobin方式輪詢選擇server | 輪詢index,選擇index對應(yīng)位置的server |
RandomRule | public class RandomRule extends AbstractLoadBalancerRule | 隨機(jī)選擇一個server | 在index上隨機(jī)举畸,選擇index對應(yīng)位置的server |
ZoneAvoidanceRule | public class ZoneAvoidanceRule extends PredicateBasedRule | 復(fù)合判斷server所在區(qū)域的性能和server的可用性選擇server | 使用ZoneAvoidancePredicate和AvailabilityPredicate來判斷是否選擇某個server查排,前一個判斷判定一個zone的運(yùn)行性能是否可用,剔除不可用的zone(的所有server)抄沮,AvailabilityPredicate用于過濾掉連接數(shù)過多的Server跋核。 |
我們可以通過修改配置來調(diào)整Ribbon的負(fù)載均衡策略岖瑰,具體代碼如下:
service-product: # 調(diào)用的提供者的名稱
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
四、服務(wù)調(diào)用 Open Feign
Feign是Spring Cloud提供的一個聲明式的偽Http客戶端砂代,它使得調(diào)用遠(yuǎn)程服務(wù)就像調(diào)用本地服務(wù)一樣簡單蹋订,只需要創(chuàng)建一個接口并添加一個注解即可。
Nacos很好的兼容了Feign刻伊,F(xiàn)eign默認(rèn)集成了 Ribbon露戒,所以在Nacos下使用Fegin默認(rèn)就實(shí)現(xiàn)了負(fù)載均衡的效果。
4.1 使用
1捶箱、在order微服務(wù)中引入Feign的依賴
<!--fegin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2智什、在主啟動類上添加@EnableFeignClients
注解,由于Feign會自動采用Ribbon做負(fù)載丁屎,所以就可以不用RestTemplate了荠锭。
@EnableFeignClients
@EntityScan("cn.x5456.common")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
// @Bean
// @LoadBalanced
// public RestTemplate getRestTemplate() {
// return new RestTemplate();
// }
}
3、創(chuàng)建一個接口
//value用于指定調(diào)用nacos下哪個微服務(wù)
@FeignClient(value = "service-product")
public interface ProductClient {
//@FeignClient的value + @RequestMapping的value值 其實(shí)就是完成的請求地址 "http://service-product/product/" + pid
//指定請求的URI部分
@RequestMapping("/product/{pid}")
Product findByPid(@PathVariable("pid") Integer pid);
}
4晨川、修改order
方法证九,使用Feign進(jìn)行調(diào)用
@Autowired
private ProductClient productClient;
//下單--fegin
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
log.info("接收到{}號商品的下單請求,接下來調(diào)用商品微服務(wù)查詢此商品信息", pid);
//調(diào)用商品微服務(wù),查詢商品信息
Product product = productClient.findByPid(pid);
log.info("查詢到{}號商品的信息,內(nèi)容是:{}", pid, JSON.toJSONString(product));
//下單(創(chuàng)建訂單)
Order order = new Order();
order.setUid(1);
order.setUsername("測試用戶");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
log.info("創(chuàng)建訂單成功,訂單信息為{}", JSON.toJSONString(order));
return order;
}
五、服務(wù)容錯 Sentinel
5.1 服務(wù)雪崩效應(yīng)
在微服務(wù)架構(gòu)中础爬,我們將業(yè)務(wù)拆分成一個個的服務(wù)甫贯,服務(wù)與服務(wù)之間可以相互調(diào)用,但是由于網(wǎng)絡(luò)原因或者自身的原因看蚜,服務(wù)并不能保證服務(wù)的100%可用叫搁,如果單個服務(wù)出現(xiàn)問題,調(diào)用這個服務(wù)就會出現(xiàn)網(wǎng)絡(luò)延遲供炎,此時若有大量的網(wǎng)絡(luò)涌入渴逻,會形成任務(wù)堆積,最終導(dǎo)致服務(wù)癱瘓音诫。
由于服務(wù)與服務(wù)之間的依賴性惨奕,故障會傳播,會對整個微服務(wù)系統(tǒng)造成災(zāi)難性的嚴(yán)重后果竭钝,這就是服務(wù)故障的“雪崩效應(yīng)” 梨撞。
雪崩發(fā)生的原因多種多樣,有不合理的容量設(shè)計(jì)香罐,或者是高并發(fā)下某一個方法響應(yīng)變慢卧波,亦或是某臺機(jī)器的資源耗盡。我們無法完全杜絕雪崩源頭的發(fā)生庇茫,只有做好足夠的容錯港粱,保證在一個服務(wù)發(fā)生問題,不會影響到其它服務(wù)的正常運(yùn)行旦签。也就是"雪落而不雪崩"查坪。
5.2 常見的容錯方案
容錯說白了就是保護(hù)自己不被豬隊(duì)友拖垮的一些措施寸宏,下面介紹常見的服務(wù)容錯思路和組件。
常見的容錯思路
1偿曙、隔離
它是指將系統(tǒng)按照一定的原則劃分為若干個服務(wù)模塊氮凝,各個模塊之間相對獨(dú)立,無強(qiáng)依賴望忆。當(dāng)有故障發(fā)生時覆醇,能將問題和影響隔離在某個模塊內(nèi)部,而不擴(kuò)散風(fēng)險(xiǎn)炭臭,不波及其它模塊永脓,不影響整體的系統(tǒng)服務(wù)。常見的隔離方式有:線程池隔離和信號量隔離鞋仍。
2常摧、超時
在上游服務(wù)調(diào)用下游服務(wù)的時候,設(shè)置一個最大響應(yīng)時間威创,如果超過這個時間落午,下游未作出反應(yīng),就斷開請求肚豺,釋放掉線程溃斋。
3、限流
限流就是限制系統(tǒng)的輸入和輸出流量已達(dá)到保護(hù)系統(tǒng)的目的吸申。為了保證系統(tǒng)的穩(wěn)固運(yùn)行梗劫,一旦達(dá)到的需要限制的閾值,就需要限制流量并采取少量措施以完成限制流量的目的截碴。
4梳侨、熔斷
在互聯(lián)網(wǎng)系統(tǒng)中,當(dāng)下游服務(wù)因訪問壓力過大而響應(yīng)變慢或失敗日丹,上游服務(wù)為了保護(hù)系統(tǒng)整 體的可用性走哺,可以暫時切斷對下游服務(wù)的調(diào)用。這種犧牲局部哲虾,保全整體的措施就叫做熔斷丙躏。
服務(wù)熔斷一般有三種狀態(tài):
- 熔斷關(guān)閉狀態(tài)(Closed) :服務(wù)沒有故障時,熔斷器所處的狀態(tài)束凑,對調(diào)用方的調(diào)用不做任何限制
- 熔斷開啟狀態(tài)(Open) :后續(xù)對該服務(wù)接口的調(diào)用不再經(jīng)過網(wǎng)絡(luò)晒旅,直接執(zhí)行本地的fallback方法
- 半熔斷狀態(tài)(Half-Open):嘗試恢復(fù)服務(wù)調(diào)用,允許有限的流量調(diào)用該服務(wù)湘今,并監(jiān)控調(diào)用成功率敢朱。如果成功率達(dá)到預(yù)期剪菱,則說明服務(wù)已恢復(fù)摩瞎,進(jìn)入熔斷關(guān)閉狀態(tài)拴签;如果成功率仍舊很低,則重新進(jìn)入熔斷關(guān)閉狀態(tài)旗们。
5蚓哩、降級
降級其實(shí)就是為服務(wù)提供一個托底方案,一旦服務(wù)無法正常調(diào)用上渴,就使用托底方案岸梨。
常見的容錯組件
Hystrix:
Hystrix是由Netflix開源的一個延遲和容錯庫,用于隔離訪問遠(yuǎn)程系統(tǒng)稠氮、服務(wù)或者第三方庫曹阔,防止級聯(lián)失敗,從而提升系統(tǒng)的可用性與容錯性隔披。
Resilience4J:
Resilicence4J一款非常輕量赃份、簡單,并且文檔非常清晰奢米、豐富的熔斷工具抓韩,這也是Hystrix官方推薦的替代產(chǎn)品。不僅如此鬓长,Resilicence4j還原生支持Spring Boot 1.x/2.x谒拴,而且監(jiān)控也支持和 prometheus等多款主流產(chǎn)品進(jìn)行整合。
Sentinel:
Sentinel 是阿里巴巴開源的一款斷路器實(shí)現(xiàn)涉波,本身在阿里內(nèi)部已經(jīng)被大規(guī)模采用英上,非常穩(wěn)定。
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔離策略 | 信號量隔離(并發(fā)線程數(shù)限流) | 線程池隔離/信號量隔離 | 信號量隔離 |
熔斷降級策略 | 基于響應(yīng)時間啤覆、異常比率善延、異常數(shù)等 | 異常比率模式、超時熔斷 | 基于異常比率城侧、響應(yīng)時間 |
實(shí)時統(tǒng)計(jì)實(shí)現(xiàn) | 滑動窗口(LeapArray) | 滑動窗口(基于 RxJava) | Ring Bit Buffer |
動態(tài)規(guī)則配置 | 支持多種配置源 | 支持多種數(shù)據(jù)源 | 有限支持 |
擴(kuò)展性 | 豐富的 SPI 擴(kuò)展接口 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于 QPS易遣,支持基于調(diào)用關(guān)系的限流 | 有限的支持 | Rate Limiter |
集群流量控制 | 支持 | 不支持 | 不支持 |
流量整形 | 支持預(yù)熱模式、勻速排隊(duì)模式等多種復(fù)雜場景 | 不支持 | 簡單的 Rate Limiter 模式 |
系統(tǒng)自適應(yīng)保護(hù) | 支持 | 不支持 | 不支持 |
控制臺 | 提供開箱即用的控制臺嫌佑,可配置規(guī)則豆茫、查看秒級監(jiān)控、機(jī)器發(fā)現(xiàn)等 | 簡單的監(jiān)控查看 | 不提供控制臺屋摇,可對接其它監(jiān)控系統(tǒng) |
多語言支持 | Java / C++ | Java | Java |
開源社區(qū)狀態(tài) | 活躍 | 停止維護(hù) | 較活躍 |
5.3 Sentinel簡介&安裝
Sentinel (分布式系統(tǒng)的流量防衛(wèi)兵) 是阿里開源的一套用于服務(wù)容錯的綜合性解決方案揩魂。它以流量為切入點(diǎn),從流量控制炮温、熔斷降級火脉、系統(tǒng)負(fù)載保護(hù)等多個維度來保護(hù)服務(wù)的穩(wěn)定性。
Sentinel 具有以下特征:
豐富的應(yīng)用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)倦挂、消息削峰填谷畸颅、集群流量控制、實(shí)時熔斷下游不可用應(yīng)用等方援。
完備的實(shí)時監(jiān)控:Sentinel 提供了實(shí)時的監(jiān)控功能没炒。通過控制臺可以看到接入應(yīng)用的單臺機(jī)器秒級數(shù)據(jù),甚至 500 臺以下規(guī)模的集群的匯總運(yùn)行情況犯戏。
廣泛的開源生態(tài):Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊送火,例如與 Spring Cloud、Dubbo先匪、gRPC 的整合种吸。只需要引入相應(yīng)的依賴并進(jìn)行簡單的配置即可快速地接入Sentinel。
完善的 SPI 擴(kuò)展點(diǎn):Sentinel 提供簡單易用呀非、完善的 SPI 擴(kuò)展接口骨稿。您可以通過實(shí)現(xiàn)擴(kuò)展接口來快速地定制邏輯。例如定制規(guī)則管理姜钳、適配動態(tài)數(shù)據(jù)源等坦冠。
Sentinel 分為兩個部分:
核心庫(Java 客戶端)不依賴任何框架/庫,能夠運(yùn)行于所有 Java 運(yùn)行時環(huán)境哥桥,同時對 Dubbo / Spring Cloud 等框架也有較好的支持辙浑。
控制臺(Dashboard)基于 Spring Boot 開發(fā),打包后可以直接運(yùn)行拟糕,不需要額外的 Tomcat 等應(yīng)用容器判呕。
安裝Sentinel控制臺
見項(xiàng)目的docker文件夾中。
安裝完成后送滞,訪問http://127.0.0.1:8099/侠草,默認(rèn)密碼為sentinel/sentinel
Sentinel的控制臺其實(shí)就是一個SpringBoot編寫的程序。我們需要將我們的微服務(wù)程序注冊到控制臺上犁嗅,即在微服務(wù)中指定控制臺的地址边涕,并且還要開啟一個跟控制臺傳遞數(shù)據(jù)的端口,控制臺也可以通過此端口調(diào)用微服務(wù)中的監(jiān)控程序獲取微服務(wù)的各種信息褂微。
將order微服務(wù)注冊到Sentinel控制臺 & 簡單使用
1功蜓、引入依賴
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、編寫OrderController2
@RestController
@Slf4j
public class OrderController2 {
@RequestMapping("/order/message1")
public String message1() {
return "message1";
}
@RequestMapping("/order/message2")
public String message2() {
return "message2";
}
}
3宠蚂、在application.yml添加配置
spring:
cloud:
sentinel:
transport:
port: 9999 #跟控制臺交流的端口,隨意指定一個未使用的端口即可
dashboard: localhost:8099 # 指定控制臺服務(wù)的地址
4式撼、啟動訂單微服務(wù),訪問http://127.0.0.1:8091/order/message1
求厕,刷新控制臺頁面
5著隆、為/order/message1
接口添加一個流控規(guī)則
6扰楼、快速訪問http://127.0.0.1:8091/order/message1
5.4 Sentinel的基本概念&功能
基本概念
資源:資源是 Sentinel 的關(guān)鍵概念。它可以是 Java 應(yīng)用程序中的任何內(nèi)容美浦,可以是一個服務(wù)弦赖,也可以是一個方法,甚至可以是一段代碼抵代。換句話說資源就是Sentinel要保護(hù)的東西。
注:上面那個demo中
message1
方法就可以認(rèn)為是一個資源
規(guī)則:規(guī)則作用在資源之上忘嫉,定義以什么樣的方式保護(hù)資源荤牍,主要包括流量控制規(guī)則、熔斷降級規(guī)則以及系統(tǒng)保護(hù)規(guī)則庆冕。
注:上面那個demo中就是為message1資源設(shè)置了一種流控規(guī)則康吵,限制了進(jìn)入message1的流量
功能(可以聯(lián)系5.2閱讀)
流量控制:
流量控制在網(wǎng)絡(luò)傳輸中是一個常用的概念,它用于調(diào)整網(wǎng)絡(luò)包的數(shù)據(jù)访递。任意時間到來的請求往往是隨機(jī)不可控的晦嵌,而系統(tǒng)的處理能力是有限的。我們需要根據(jù)系統(tǒng)的處理能力對流量進(jìn)行控制拷姿。
熔斷降級:
當(dāng)檢測到調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定的表現(xiàn)惭载,例如請求響應(yīng)時間長或異常比例升高的時候,則對這個資源的調(diào)用進(jìn)行限制响巢,讓請求快速失敗描滔,避免影響到其它的資源而導(dǎo)致級聯(lián)故障。
Sentinel 對這個問題采取了兩種手段:
-
通過并發(fā)線程數(shù)進(jìn)行限制:
- Sentinel 通過限制資源并發(fā)線程的數(shù)量踪古,來減少不穩(wěn)定資源對其它資源的影響含长。當(dāng)某個資源出現(xiàn)不穩(wěn)定的情況下,例如響應(yīng)時間變長伏穆,對資源的直接影響就是會造成線程數(shù)的逐步堆積拘泞。當(dāng)線程數(shù)在特定資源上堆積到一定的數(shù)量之后,對該資源的新請求就會被拒絕枕扫。堆積的線程完成任務(wù)后才開始繼續(xù)接收請求陪腌。
-
通過響應(yīng)時間對資源進(jìn)行降級:
- 除了對并發(fā)線程數(shù)進(jìn)行控制以外,Sentinel 還可以通過響應(yīng)時間來快速降級不穩(wěn)定的資源烟瞧。當(dāng)依賴的資源出現(xiàn)響應(yīng)時間過長后偷厦,所有對該資源的訪問都會被直接拒絕,直到過了指定的時間窗口之后才重新恢復(fù)燕刻。
注:Sentinel 和 Hystrix 的區(qū)別只泼?
兩者的原則是一致的,都是當(dāng)一個資源出現(xiàn)問題時卵洗,讓其快速失敗请唱,不要波及到其它服務(wù)弥咪。
但是在限制的手段上,確采取了完全不一樣的方法:
Hystrix 采用的是線程池隔離的方式十绑,優(yōu)點(diǎn)是做到了資源之間的隔離诚镰,缺點(diǎn)是增加了線程切換的成本。
Sentinel 采用的是通過并發(fā)線程的數(shù)量和響應(yīng)時間來對資源做限制切平。(信號量隔離)
系統(tǒng)負(fù)載保護(hù)
Sentinel 同時提供系統(tǒng)維度的自適應(yīng)保護(hù)能力锅减。當(dāng)系統(tǒng)負(fù)載較高的時候,如果還持續(xù)讓請求進(jìn)入可能會導(dǎo)致系統(tǒng)崩潰甚亭,無法響應(yīng)贷币。在集群環(huán)境下,會把本應(yīng)這臺機(jī)器承載的流量轉(zhuǎn)發(fā)到其它的機(jī)器上去亏狰。如果這個時候其它的機(jī)器也處在一個邊緣狀態(tài)的時候役纹,Sentinel 提供了對應(yīng)的保護(hù)機(jī)制,讓系統(tǒng)的入口流量和系統(tǒng)的負(fù)載達(dá)到一個平衡暇唾,保證系統(tǒng)在能力范圍之內(nèi)處理最多的請求促脉。
注:系統(tǒng)的負(fù)載能力,與其所在的服務(wù)器內(nèi)存和CPU等外界環(huán)境有關(guān)策州。
5.5 Sentinel規(guī)則
5.5.1 流控規(guī)則
流量控制瘸味,其原理是監(jiān)控應(yīng)用流量的QPS(每秒查詢率) 或并發(fā)線程數(shù)等指標(biāo),當(dāng)達(dá)到指定的閾值時對流量進(jìn)行控制够挂,以避免被瞬時的流量高峰沖垮硫戈,從而保障應(yīng)用的高可用性。
資源名:唯一名稱下硕,默認(rèn)是請求路徑丁逝,可自定義
針對來源;指定對哪個微服務(wù)進(jìn)行限流梭姓,默認(rèn)指default霜幼,意思是不區(qū)分來源,全部限制
閾值類型/單機(jī)閾值:
- QPS(每秒請求數(shù)量):當(dāng)調(diào)用該接口的QPS達(dá)到閾值的時候誉尖,進(jìn)行限流
- 線程數(shù):當(dāng)調(diào)用該接口的線程數(shù)達(dá)到閾值的時候罪既,進(jìn)行限流
是否集群:暫不需要集群
配置好后,瘋狂請求/order/message1接口時铡恕,就會出現(xiàn)下面的情況:
3種流控模式
1琢感、直接流控模式
直接流控模式是最簡單的模式,當(dāng)指定的接口達(dá)到限流條件時開啟限流探熔。就是我們上面演示的例子驹针。
2、關(guān)聯(lián)流控模式
關(guān)聯(lián)流控模式指的是诀艰,當(dāng)指定接口關(guān)聯(lián)的接口達(dá)到限流條件時柬甥,開啟對指定接口開啟限流饮六。
當(dāng)/order/message2
接口的OPS大于1的時候,/order/message1
就會被限流苛蒲。
3卤橄、鏈路流控模式
鏈路流控模式指的是,當(dāng)從某個接口過來的資源達(dá)到限流條件時臂外,開啟限流窟扑。它的功能有點(diǎn)類似于針對來源配置項(xiàng),區(qū)別在于:針對來源是針對上級微服務(wù)漏健,而鏈路流控是針對上級接口嚎货,也就是說它的粒度更細(xì)。
1)編寫OrderService
@Service
public class OrderServiceImpl {
// 定義資源 value是資源的名稱
@SentinelResource("message")
public void message() {
System.out.println("message");
}
}
2)修改OrderController2中的2個方法漾肮,調(diào)用service層的message方法厂抖。
@RestController
@Slf4j
public class OrderController2 {
@Autowired
private OrderServiceImpl orderService;
@RequestMapping("/order/message1")
public String message1() {
orderService.message();
return "message1";
}
@RequestMapping("/order/message2")
public String message2() {
orderService.message();
return "message2";
}
}
3)禁止收斂URL的入口 context
從1.6.3 版本開始茎毁,Sentinel Web filter默認(rèn)收斂所有URL的入口context克懊,因此鏈路限流不生效。
1.7.0 版本開始(對應(yīng)SCA的2.1.1.RELEASE)七蜘,官方在CommonFilter 引入了 WEB_CONTEXT_UNIFY 參數(shù)谭溉,用于控制是否收斂context。將其配置為 false 即可根據(jù)不同的URL 進(jìn)行鏈路限流橡卤。
SCA 2.1.1.RELEASE之后的版本,可以通過配置spring.cloud.sentinel.web-context-unify=false即可關(guān)閉收斂我們當(dāng)前使用的版本是SpringCloud Alibaba 2.1.0.RELEASE扮念,無法實(shí)現(xiàn)鏈路限流。
目前官方還未發(fā)布SCA 2.1.2.RELEASE碧库,所以我們只能使用2.1.1.RELEASE柜与,需要寫代碼的形式實(shí)現(xiàn)
下面的修改,測試完畢之后要改回來嵌灰;生產(chǎn)環(huán)境最好還是等2.1.2.RELEASE版本發(fā)布弄匕,配合Cloud Hoxton使用。
(1)將SpringCloud Alibaba的版本調(diào)整為2.1.1.RELEASE
<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>
(2)配置文件中關(guān)閉sentinel的CommonFilter實(shí)例化
spring:
cloud:
sentinel:
filter:
enabled: false
(3)手動注入CommonFilter的實(shí)例
@Configuration
public class FilterContextConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
// 入口資源關(guān)閉聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
4)重啟order微服務(wù)沽瞭,訪問http://127.0.0.1:8091/order/message1
5)配置鏈路限流
6)狂點(diǎn)刷新
3種流控效果
快速失敗(默認(rèn)):直接失敗迁匠,拋出異常,不做任何額外的處理驹溃,是最簡單的效果
Warm Up:它從開始閾值到最大QPS閾值會有一個緩沖階段城丧,一開始的閾值是最大QPS閾值的1/3,然后慢慢增長豌鹤,直到最大閾值亡哄,適用于將突然增大的流量轉(zhuǎn)換為緩步增長的場景。
排隊(duì)等待:讓請求以均勻的速度通過布疙,單機(jī)閾值為每秒通過數(shù)量磺平,其余的排隊(duì)等待; 它還會讓設(shè)置一個超時時間魂仍,當(dāng)請求超過超時間時間還未處理,則會被丟棄拣挪。
5.5.2 降級規(guī)則
降級規(guī)則就是設(shè)置當(dāng)滿足什么條件的時候擦酌,對服務(wù)進(jìn)行降級。Sentinel提供了三個衡量條件:
平均響應(yīng)時間(RT)
當(dāng)資源的平均響應(yīng)時間超過閾值(以 ms 為單位)之后菠劝,資源進(jìn)入準(zhǔn)降級狀態(tài)赊舶。如果接下來 1s 內(nèi)持續(xù)進(jìn)入 5 個請求,它們的 RT都持續(xù)超過這個閾值赶诊,那么在接下的時間窗口(以 s 為單位))之內(nèi)笼平,就會對這個方法進(jìn)行服務(wù)降級。
注:注意 Sentinel 默認(rèn)統(tǒng)計(jì)的 RT 上限是 4900 ms舔痪,超出此閾值的都會算作 4900 ms寓调,若需要變更此上限可以通過啟動配置項(xiàng) -Dcsp.sentinel.statistic.max.rt=xxx 來配置。
異常比例
當(dāng)資源的每秒異吵耄總數(shù)占通過量的比值超過閾值之后夺英,資源進(jìn)入降級狀態(tài),即在接下的時間窗口(以 s 為單位)之內(nèi)滋捶,對這個方法的調(diào)用都會自動地返回痛悯。異常比率的閾值范圍是[0.0, 1.0]。
異常數(shù)
當(dāng)資源近 1 分鐘的異常數(shù)目超過閾值之后會進(jìn)行服務(wù)降級重窟。注意由于統(tǒng)計(jì)時間窗口是分鐘級別的载萌,若時間窗口小于 60s,則結(jié)束熔斷狀態(tài)后仍可能再進(jìn)入熔斷狀態(tài)巡扇。
5.5.3 熱點(diǎn)規(guī)則
熱點(diǎn)參數(shù)流控規(guī)則是一種更細(xì)粒度的流控規(guī)則扭仁,它允許將規(guī)則具體到參數(shù)上。
1厅翔、在OrderController2中新增一個方法乖坠,記得添加@SentinelResource
注解
@RequestMapping("/order/message3")
@SentinelResource("message3")
public String message3(String name, Integer age) {
return "message3" + name + age;
}
2、配置熱點(diǎn)規(guī)則
單機(jī)閥值:每秒訪問量進(jìn)行限流
統(tǒng)計(jì)窗口時長:超過多長時間知给,恢復(fù)訪問
5.5.4 授權(quán)規(guī)則
很多時候瓤帚,我們需要根據(jù)調(diào)用來源來判斷該次請求是否允許放行,例如知網(wǎng)會根據(jù)請求的refer來判斷是爬蟲還是正常的用戶訪問涩赢,從而返回不同的頁面戈次,這時候可以使用 Sentinel 的來源訪問控制的功能。來源訪問控制根據(jù)資源的請求來源(origin)限制資源是否通過:
- 若配置白名單筒扒,則只有請求來源位于白名單內(nèi)時才可通過;
- 若配置黑名單怯邪,則請求來源位于黑名單時不通過,其余的請求通過花墩。
1悬秉、OrderController中新增一個方法
@RequestMapping("/order/message4")
public String message4() {
return "message4";
}
2澄步、實(shí)現(xiàn)RequestOriginParser,定義解析規(guī)則
@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
//定義區(qū)分來源: 本質(zhì)作用是通過request域獲取到來源標(biāo)識
//app pc
//然后 交給流控應(yīng)用 位置進(jìn)行匹配
@Override
public String parseOrigin(HttpServletRequest request) {
return request.getParameter("serviceName");
}
}
3和泌、重啟系統(tǒng)村缸,訪問 http://127.0.0.1:8091/order/message4
4、授權(quán)
5武氓、當(dāng)訪問 http://127.0.0.1:8091/order/message4?serviceName=pc 可以正常訪問梯皿,訪問其他的會失敗。
5.5.5 系統(tǒng)規(guī)則(一般運(yùn)維配置)
系統(tǒng)保護(hù)規(guī)則是從應(yīng)用級別的入口流量進(jìn)行控制县恕,從單臺機(jī)器的總體 Load东羹、RT、入口 QPS 忠烛、CPU 使用率和線程數(shù)五個維度監(jiān)控應(yīng)用數(shù)據(jù)属提,讓系統(tǒng)盡可能跑在最大吞吐量的同時保證系統(tǒng)整體的穩(wěn)定性。
系統(tǒng)保護(hù)規(guī)則是應(yīng)用整體維度的美尸,而不是資源維度的冤议,并且僅對入口流量 (進(jìn)入應(yīng)用的流量)生效。
- Load(僅對 Linux/Unix-like 機(jī)器生效):當(dāng)系統(tǒng) load1(1min閾值)超過閾值火惊,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過系統(tǒng)容量時才會觸發(fā)系統(tǒng)保護(hù)求类。系統(tǒng)容量由系統(tǒng)的 maxQps * minRt 計(jì)算得出奔垦。設(shè)定參考值一般 是 CPU cores * 2.5屹耐。
- RT:當(dāng)單臺機(jī)器上所有入口流量的平均 RT 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒椿猎。
- 線程數(shù):當(dāng)單臺機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)惶岭。
- 入口 QPS:當(dāng)單臺機(jī)器上所有入口流量的 QPS 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
- CPU使用率:當(dāng)單臺機(jī)器上所有入口流量的 CPU使用率達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)犯眠。
擴(kuò)展:自定義異常返回
問題:流控規(guī)則和降級規(guī)則返回的異常頁面是一樣的按灶,我們怎么來區(qū)分到底是什么原因?qū)е碌哪?
//自定義異常返回頁面
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
// BlockException是五種規(guī)則異常的父類
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
response.setContentType("application/json;charset=utf-8");
ResponseData responseData = null;
//BlockException 異常接口,包含Sentinel的五個異常
// FlowException 限流異常
// DegradeException 降級異常
// ParamFlowException 參數(shù)限流異常
// AuthorityException 授權(quán)異常
// SystemBlockException 系統(tǒng)負(fù)載異常
if (e instanceof FlowException) {
responseData = new ResponseData(-1, "接口被限流了...");
} else if (e instanceof DegradeException) {
responseData = new ResponseData(-2, "接口被降級了...");
}
response.getWriter().write(JSON.toJSONString(responseData));
}
}
@Data
@AllArgsConstructor//全參構(gòu)造
@NoArgsConstructor
//無參構(gòu)造
class ResponseData {
private int code;
private String message;
}
5.6 @SentinelResource的使用
在定義了資源點(diǎn)之后,我們可以通過Dashboard來設(shè)置限流和降級策略來對資源點(diǎn)進(jìn)行保護(hù)筐咧。同時還能通過@SentinelResource來指定出現(xiàn)異常時的處理策略鸯旁。
屬性 | 作用 | 是否必須 |
---|---|---|
value | 資源名稱 | 是 |
entryType | entry類型,標(biāo)記流量的方向量蕊,取值IN/OUT铺罢,默認(rèn)是OUT | 否 |
blockHandler | 處理BlockException的函數(shù)名稱。函數(shù)要求: 1. 必須是 public 2.返回類型與原方法一致 3. 參數(shù)類型需要和原方法相匹配残炮,并在最后加 BlockException 類型的參數(shù)韭赘。 4. 默認(rèn)需和原方法在同一個類中。若希望使用其他類的函數(shù)势就,可配置 blockHandlerClass 泉瞻,并指定blockHandlerClass里面的方法脉漏。 |
否 |
blockHandlerClass | 存放blockHandler的類。對應(yīng)的處理函數(shù)必須static修飾袖牙,否則無法解析侧巨,其他要求:同blockHandler。 | 否 |
fallback | 用于在拋出異常的時候提供fallback處理邏輯鞭达。fallback函數(shù)可以針對所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進(jìn)行處理刃泡。函數(shù)要求: 1. 返回類型與原方法一致 2. 參數(shù)類型需要和原方法相匹配,Sentinel 1.6開始碉怔,也可在方法最后加 Throwable 類型的參數(shù)烘贴。 3.默認(rèn)需和原方法在同一個類中。若希望使用其他類的函數(shù)撮胧,可配置 fallbackClass 桨踪,并指定fallbackClass里面的方法。 |
否 |
fallbackClass【1.6】 | 存放fallback的類芹啥。對應(yīng)的處理函數(shù)必須static修飾锻离,否則無法解析,其他要求:同fallback墓怀。 | 否 |
defaultFallback【1.6】 | 用于通用的 fallback 邏輯汽纠。默認(rèn)fallback函數(shù)可以針對所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進(jìn)行處理。若同時配置了 fallback 和 defaultFallback傀履,以fallback為準(zhǔn)虱朵。函數(shù)要求: 1. 返回類型與原方法一致 2. 方法參數(shù)列表為空,或者有一個 Throwable 類型的參數(shù)钓账。 3. 默認(rèn)需要和原方法在同一個類中碴犬。若希望使用其他類的函數(shù),可配置 fallbackClass 梆暮,并指定 fallbackClass 里面的方法服协。 |
否 |
exceptionsToIgnore【1.6】 | 指定排除掉哪些異常。排除的異常不會計(jì)入異常統(tǒng)計(jì)啦粹,也不會進(jìn)入fallback邏輯偿荷,而是原樣拋出。 | 否 |
exceptionsToTrace | 需要trace的異常 | Throwable |
5.6.1 示例
1唠椭、修改OrderServiceImpl中message方法上的注解跳纳,添加幾個參數(shù)
@Slf4j
@Service
public class OrderServiceImpl {
//定義一個資源
//定義當(dāng)資源內(nèi)部發(fā)生異常的時候的處理邏輯
//blockHandler 定義當(dāng)資源內(nèi)部發(fā)生了BlockException應(yīng)該進(jìn)入的方法[捕獲的是Sentinel定義的異常]
//fallback 定義當(dāng)資源內(nèi)部發(fā)生了Throwable應(yīng)該進(jìn)入的方法
@SentinelResource(
value = "message",
blockHandler = "blockHandler",
fallback = "fallback"
)
public String message() {
return "message";
}
//blockHandler
//要求:
//1 當(dāng)前方法的返回值和參數(shù)要跟原方法一致
//2 但是允許在參數(shù)列表的最后加入一個參數(shù)BlockException, 用來接收原方法中發(fā)生的異常
public String blockHandler(BlockException e) {
//自定義異常處理邏輯
log.error("觸發(fā)了BlockException,內(nèi)容為{}", e);
return "BlockException";
}
//fallback
//要求:
//1 當(dāng)前方法的返回值和參數(shù)要跟原方法一致
//2 但是允許在參數(shù)列表的最后加入一個參數(shù)BlockException, 用來接收原方法中發(fā)生的異常
public String fallback(Throwable e) {
//自定義異常處理邏輯
log.error("觸發(fā)了Throwable,內(nèi)容為{}", e);
return "Throwable";
}
}
2、修改OrderController2中的message1()方法
@RequestMapping("/order/message1")
public String message1() {
return orderService.message();
}
3泪蔫、去控制臺添加流控規(guī)則
4棒旗、快速訪問 http://127.0.0.1:8091/order/message1
5.6.2 將限流和降級方法外置到單獨(dú)的類中
@Slf4j
@Service
public class OrderServiceImpl {
//定義一個資源
//定義當(dāng)資源內(nèi)部發(fā)生異常的時候的處理邏輯
//blockHandler 定義當(dāng)資源內(nèi)部發(fā)生了BlockException應(yīng)該進(jìn)入的方法[捕獲的是Sentinel定義的異常]
//fallback 定義當(dāng)資源內(nèi)部發(fā)生了Throwable應(yīng)該進(jìn)入的方法
@SentinelResource(
value = "message",
blockHandlerClass = OrderServiceImpl3BlockHandler.class,
blockHandler = "blockHandler",
fallbackClass = OrderServiceImpl3Fallback.class,
fallback = "fallback"
)
public String message() {
return "message";
}
public static class OrderServiceImpl3BlockHandler {
//blockHandler
//要求:
//1 當(dāng)前方法的返回值和參數(shù)要跟原方法一致
//2 但是允許在參數(shù)列表的最后加入一個參數(shù)BlockException, 用來接收原方法中發(fā)生的異常
public static String blockHandler(BlockException e) {
//自定義異常處理邏輯
log.error("觸發(fā)了BlockException,內(nèi)容為{}", e);
return "BlockException";
}
}
public static class OrderServiceImpl3Fallback {
//fallback
//要求:
//1 當(dāng)前方法的返回值和參數(shù)要跟原方法一致
//2 但是允許在參數(shù)列表的最后加入一個參數(shù)BlockException, 用來接收原方法中發(fā)生的異常
public static String fallback(Throwable e) {
//自定義異常處理邏輯
log.error("觸發(fā)了Throwable,內(nèi)容為{}", e);
return "Throwable";
}
}
}
注:類中的方法必須是static的
5.7 Sentinel規(guī)則持久化
Sentinel規(guī)則默認(rèn)是存放在內(nèi)存中,極不穩(wěn)定,所以需要將其持久化铣揉。
本地文件數(shù)據(jù)源會定時輪詢文件的變更饶深,讀取規(guī)則。這樣我們既可以在應(yīng)用本地直接修改文件來更新規(guī)則逛拱,也可以通過 Sentinel 控制臺推送規(guī)則敌厘。以本地文件數(shù)據(jù)源為例,推送過程如下圖所示:
首先 Sentinel 控制臺通過 API 將規(guī)則推送至客戶端并更新到內(nèi)存中朽合,接著注冊的寫數(shù)據(jù)源會將新的規(guī)則保存到本地的文件中俱两。
1、編寫處理類
參見cn.x5456.order.config.FilePersistence
2曹步、添加配置
在resources下創(chuàng)建配置目錄 META-INF/services宪彩,然后添加文件
com.alibaba.csp.sentinel.init.InitFunc,文件內(nèi)容為配置類的全路徑
5.8 Feign整合Sentinel
1讲婚、開啟Feign對Sentinel的支持
# 開啟feign對sentinel的支持
feign:
sentinel:
enabled: true
2尿孔、創(chuàng)建容錯類
//這是一個容錯類
//它要求實(shí)現(xiàn)Feign所在接口,并實(shí)現(xiàn)里面的方法
//當(dāng)feign調(diào)用出現(xiàn)問題的時候,就會進(jìn)入到當(dāng)前類中同名方法中
@Component
public class ProductServiceFallback implements ProductClient {
@Override
public Product findByPid(Integer pid) {
Product product = new Product();
product.setPid(-100);
product.setPname("商品微服務(wù)調(diào)用出現(xiàn)異常了,已經(jīng)進(jìn)入到了容錯方法中");
return product;
}
}
3、修改ProductClient上的注解
//value用于指定調(diào)用nacos下哪個微服務(wù)
@FeignClient(value = "service-product", fallback = ProductServiceFallback.class)
public interface ProductClient {
//@FeignClient的value + @RequestMapping的value值 其實(shí)就是完成的請求地址 "http://service-product/product/" + pid
//指定請求的URI部分
@RequestMapping("/product/{pid}")
Product findByPid(@PathVariable("pid") Integer pid);
}
4筹麸、修改OrderController的order方法
//下單--fegin
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
log.info("接收到{}號商品的下單請求,接下來調(diào)用商品微服務(wù)查詢此商品信息", pid);
//調(diào)用商品微服務(wù),查詢商品信息
Product product = productClient.findByPid(pid);
if (product.getPid() == -100) {
Order order = new Order();
order.setOid(-100L);
order.setPname("下單失敗");
return order;
}
log.info("查詢到{}號商品的信息,內(nèi)容是:{}", pid, JSON.toJSONString(product));
//下單(創(chuàng)建訂單)
Order order = new Order();
order.setUid(1);
order.setUsername("測試用戶");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
log.info("創(chuàng)建訂單成功,訂單信息為{}", JSON.toJSONString(order));
return order;
}
5活合、啟動服務(wù),訪問 http://127.0.0.1:8091/order/prod/1物赶,由于商品微服務(wù)未開白指,所以會報(bào)錯
6、采用這種方式酵紫,異常信息被隱藏了告嘲,所以可以采用另外一種方式
1)新建ProductServiceFallbackFactory類
//這是容錯類,他要求我們要是實(shí)現(xiàn)一個FallbackFactory<要為哪個接口產(chǎn)生容錯類>
@Slf4j
@Service
public class ProductServiceFallbackFactory implements FallbackFactory<ProductClient> {
//Throwable 這就是fegin在調(diào)用過程中產(chǎn)生異常
@Override
public ProductClient create(Throwable throwable) {
return new ProductClient() {
@Override
public Product findByPid(Integer pid) {
log.error("{}",throwable);
Product product = new Product();
product.setPid(-100);
product.setPname("商品微服務(wù)調(diào)用出現(xiàn)異常了,已經(jīng)進(jìn)入到了容錯方法中");
return product;
}
};
}
}
2)修改ProductClient上的注解
//value用于指定調(diào)用nacos下哪個微服務(wù)
@FeignClient(value = "service-product", fallbackFactory = ProductServiceFallbackFactory.class)
public interface ProductClient {
//@FeignClient的value + @RequestMapping的value值 其實(shí)就是完成的請求地址 "http://service-product/product/" + pid
//指定請求的URI部分
@RequestMapping("/product/{pid}")
Product findByPid(@PathVariable("pid") Integer pid);
}
注:fallback和fallbackFactory只能使用其中一種方式