溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

發(fā)布時(shí)間:2020-06-29 20:25:03 來(lái)源:網(wǎng)絡(luò) 閱讀:3452 作者:wx5d6cccb1cb158 欄目:編程語(yǔ)言

首先我給大家看一張圖,如果大家對(duì)這張圖有些地方不太理解的話,我希望你們看完我這篇文章會(huì)恍然大悟。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

什么是Spring cloud
構(gòu)建分布式系統(tǒng)不需要復(fù)雜和容易出錯(cuò)。Spring Cloud 為最常見(jiàn)的分布式系統(tǒng)模式提供了一種簡(jiǎn)單且易于接受的編程模型,幫助開(kāi)發(fā)人員構(gòu)建有彈性的、可靠的、協(xié)調(diào)的應(yīng)用程序。Spring Cloud 構(gòu)建于 Spring Boot 之上,使得開(kāi)發(fā)者很容易入手并快速應(yīng)用于生產(chǎn)中。

官方果然官方,介紹都這么有板有眼的。

我所理解的 Spring Cloud 就是微服務(wù)系統(tǒng)架構(gòu)的一站式解決方案,在平時(shí)我們構(gòu)建微服務(wù)的過(guò)程中需要做如 服務(wù)發(fā)現(xiàn)注冊(cè) 、配置中心 、消息總線 、負(fù)載均衡 、斷路器 、數(shù)據(jù)監(jiān)控 等操作,而 Spring Cloud 為我們提供了一套簡(jiǎn)易的編程模型,使我們能在 Spring Boot 的基礎(chǔ)上輕松地實(shí)現(xiàn)微服務(wù)項(xiàng)目的構(gòu)建。

Spring Cloud 的版本
當(dāng)然這個(gè)只是個(gè)題外話。

Spring Cloud 的版本號(hào)并不是我們通常見(jiàn)的數(shù)字版本號(hào),而是一些很奇怪的單詞。這些單詞均為英國(guó)倫敦地鐵站的站名。同時(shí)根據(jù)字母表的順序來(lái)對(duì)應(yīng)版本時(shí)間順序,比如:最早 的 Release 版本 Angel,第二個(gè) Release 版本 Brixton(英國(guó)地名),然后是 Camden、 Dalston、Edgware、Finchley、Greenwich、Hoxton。

Spring Cloud 的服務(wù)發(fā)現(xiàn)框架——Eureka
Eureka是基于REST(代表性狀態(tài)轉(zhuǎn)移)的服務(wù),主要在AWS云中用于定位服務(wù),以實(shí)現(xiàn)負(fù)載均衡和中間層服務(wù)器的故障轉(zhuǎn)移。我們稱此服務(wù)為Eureka服務(wù)器。Eureka還帶有一個(gè)基于Java的客戶端組件Eureka Client,它使與服務(wù)的交互變得更加容易??蛻舳诉€具有一個(gè)內(nèi)置的負(fù)載平衡器,可以執(zhí)行基本的循環(huán)負(fù)載平衡。在Netflix,更復(fù)雜的負(fù)載均衡器將Eureka包裝起來(lái),以基于流量,資源使用,錯(cuò)誤條件等多種因素提供加權(quán)負(fù)載均衡,以提供出色的彈性。

總的來(lái)說(shuō),Eureka 就是一個(gè)服務(wù)發(fā)現(xiàn)框架。何為服務(wù),何又為發(fā)現(xiàn)呢?

舉一個(gè)生活中的例子,就比如我們平時(shí)租房子找中介的事情。

在沒(méi)有中介的時(shí)候我們需要一個(gè)一個(gè)去尋找是否有房屋要出租的房東,這顯然會(huì)非常的費(fèi)力,一你找憑一個(gè)人的能力是找不到很多房源供你選擇,再者你也懶得這么找下去(找了這么久,沒(méi)有合適的只能將就)。這里的我們就相當(dāng)于微服務(wù)中的 Consumer ,而那些房東就相當(dāng)于微服務(wù)中的 Provider 。消費(fèi)者 Consumer 需要調(diào)用提供者 Provider 提供的一些服務(wù),就像我們現(xiàn)在需要租他們的房子一樣。

但是如果只是租客和房東之間進(jìn)行尋找的話,他們的效率是很低的,房東找不到租客賺不到錢,租客找不到房東住不了房。所以,后來(lái)房東肯定就想到了廣播自己的房源信息(比如在街邊貼貼小廣告),這樣對(duì)于房東來(lái)說(shuō)已經(jīng)完成他的任務(wù)(將房源公布出去),但是有兩個(gè)問(wèn)題就出現(xiàn)了。第一、其他不是租客的都能收到這種租房消息,這在現(xiàn)實(shí)世界沒(méi)什么,但是在計(jì)算機(jī)的世界中就會(huì)出現(xiàn)資源消耗的問(wèn)題了。第二、租客這樣還是很難找到你,試想一下我需要租房,我還需要東一個(gè)西一個(gè)地去找街邊小廣告,麻不麻煩?
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

那怎么辦呢?我們當(dāng)然不會(huì)那么傻乎乎的,第一時(shí)間就是去找 中介 呀,它為我們提供了統(tǒng)一房源的地方,我們消費(fèi)者只需要跑到它那里去找就行了。而對(duì)于房東來(lái)說(shuō),他們也只需要把房源在中介那里發(fā)布就行了。

那么現(xiàn)在,我們的模式就是這樣的了。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

但是,這個(gè)時(shí)候還會(huì)出現(xiàn)一些問(wèn)題。

房東注冊(cè)之后如果不想賣房子了怎么辦?我們是不是需要讓房東定期續(xù)約?如果房東不進(jìn)行續(xù)約是不是要將他們從中介那里的注冊(cè)列表中移除。
租客是不是也要進(jìn)行注冊(cè)呢?不然合同乙方怎么來(lái)呢?
中介可不可以做連鎖店呢?如果這一個(gè)店因?yàn)槟承┎豢煽沽σ蛩囟鵁o(wú)法使用,那么我們是否可以換一個(gè)連鎖店呢?
針對(duì)上面的問(wèn)題我們來(lái)重新構(gòu)建一下上面的模式圖
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

好了,舉完這個(gè)我們就可以來(lái)看關(guān)于 Eureka 的一些基礎(chǔ)概念了,你會(huì)發(fā)現(xiàn)這東西理解起來(lái)怎么這么簡(jiǎn)單。

服務(wù)發(fā)現(xiàn):其實(shí)就是一個(gè)“中介”,整個(gè)過(guò)程中有三個(gè)角色:服務(wù)提供者(出租房子的)、服務(wù)消費(fèi)者(租客)、服務(wù)中介(房屋中介)。

服務(wù)提供者:就是提供一些自己能夠執(zhí)行的一些服務(wù)給外界。

服務(wù)消費(fèi)者:就是需要使用一些服務(wù)的“用戶”。

服務(wù)中介:其實(shí)就是服務(wù)提供者和服務(wù)消費(fèi)者之間的“橋梁”,服務(wù)提供者可以把自己注冊(cè)到服務(wù)中介那里,而服務(wù)消費(fèi)者如需要消費(fèi)一些服務(wù)(使用一些功能)就可以在服務(wù)中介中尋找注冊(cè)在服務(wù)中介的服務(wù)提供者。

服務(wù)注冊(cè) Register:

官方解釋:當(dāng) Eureka 客戶端向 Eureka Server 注冊(cè)時(shí),它提供自身的元數(shù)據(jù),比如IP地址、端口,運(yùn)行狀況指示符URL,主頁(yè)等。

結(jié)合中介理解:房東 (提供者 Eureka Client Provider)在中介 (服務(wù)器 Eureka Server) 那里登記房屋的信息,比如面積,價(jià)格,地段等等(元數(shù)據(jù) metaData)。

服務(wù)續(xù)約 Renew:

官方解釋:Eureka 客戶會(huì)每隔30秒(默認(rèn)情況下)發(fā)送一次心跳來(lái)續(xù)約。通過(guò)續(xù)約來(lái)告知 Eureka Server 該 Eureka 客戶仍然存在,沒(méi)有出現(xiàn)問(wèn)題。正常情況下,如果 Eureka Server 在90秒沒(méi)有收到 Eureka 客戶的續(xù)約,它會(huì)將實(shí)例從其注冊(cè)表中刪除。

結(jié)合中介理解:房東 (提供者 Eureka Client Provider) 定期告訴中介 (服務(wù)器 Eureka Server) 我的房子還租(續(xù)約) ,中介 (服務(wù)器Eureka Server) 收到之后繼續(xù)保留房屋的信息。

獲取注冊(cè)列表信息 Fetch Registries:

官方解釋:Eureka 客戶端從服務(wù)器獲取注冊(cè)表信息,并將其緩存在本地??蛻舳藭?huì)使用該信息查找其他服務(wù),從而進(jìn)行遠(yuǎn)程調(diào)用。該注冊(cè)列表信息定期(每30秒鐘)更新一次。每次返回注冊(cè)列表信息可能與 Eureka 客戶端的緩存信息不同, Eureka 客戶端自動(dòng)處理。如果由于某種原因?qū)е伦?cè)列表信息不能及時(shí)匹配,Eureka 客戶端則會(huì)重新獲取整個(gè)注冊(cè)表信息。Eureka 服務(wù)器緩存注冊(cè)列表信息,整個(gè)注冊(cè)表以及每個(gè)應(yīng)用程序的信息進(jìn)行了壓縮,壓縮內(nèi)容和沒(méi)有壓縮的內(nèi)容完全相同。Eureka 客戶端和 Eureka 服務(wù)器可以使用JSON / XML格式進(jìn)行通訊。在默認(rèn)的情況下 Eureka 客戶端使用壓縮 JSON 格式來(lái)獲取注冊(cè)列表的信息。

結(jié)合中介理解:租客(消費(fèi)者 Eureka Client Consumer) 去中介 (服務(wù)器 Eureka Server) 那里獲取所有的房屋信息列表 (客戶端列表 Eureka Client List) ,而且租客為了獲取最新的信息會(huì)定期向中介 (服務(wù)器 Eureka Server) 那里獲取并更新本地列表。

服務(wù)下線 Cancel:

官方解釋:Eureka客戶端在程序關(guān)閉時(shí)向Eureka服務(wù)器發(fā)送取消請(qǐng)求。發(fā)送請(qǐng)求后,該客戶端實(shí)例信息將從服務(wù)器的實(shí)例注冊(cè)表中刪除。該下線請(qǐng)求不會(huì)自動(dòng)完成,它需要調(diào)用以下內(nèi)容:DiscoveryManager.getInstance().shutdownComponent();

結(jié)合中介理解:房東 (提供者 Eureka Client Provider) 告訴中介 (服務(wù)器 Eureka Server) 我的房子不租了,中介之后就將注冊(cè)的房屋信息從列表中剔除。

服務(wù)剔除 Eviction:

官方解釋:在默認(rèn)的情況下,當(dāng)Eureka客戶端連續(xù)90秒(3個(gè)續(xù)約周期)沒(méi)有向Eureka服務(wù)器發(fā)送服務(wù)續(xù)約,即心跳,Eureka服務(wù)器會(huì)將該服務(wù)實(shí)例從服務(wù)注冊(cè)列表刪除,即服務(wù)剔除。

結(jié)合中介理解:房東(提供者 Eureka Client Provider) 會(huì)定期聯(lián)系 中介 (服務(wù)器 Eureka Server) 告訴他我的房子還租(續(xù)約),如果中介 (服務(wù)器 Eureka Server) 長(zhǎng)時(shí)間沒(méi)收到提供者的信息,那么中介會(huì)將他的房屋信息給下架(服務(wù)剔除)。

下面就是 Netflix 官方給出的 Eureka 架構(gòu)圖,你會(huì)發(fā)現(xiàn)和我們前面畫(huà)的中介圖別無(wú)二致。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

當(dāng)然,可以充當(dāng)服務(wù)發(fā)現(xiàn)的組件有很多:Zookeeper ,Consul , Eureka 等。

更多關(guān)于 Eureka 的知識(shí)(自我保護(hù),初始注冊(cè)策略等等)可以自己去官網(wǎng)查看。

負(fù)載均衡之 Ribbon
什么是 RestTemplate?
不是講 Ribbon 么?怎么扯到了 RestTemplate 了?你先別急,聽(tīng)我慢慢道來(lái)。

我不聽(tīng)我不聽(tīng)我不聽(tīng)。

我就說(shuō)一句!RestTemplate是Spring提供的一個(gè)訪問(wèn)Http服務(wù)的客戶端類,怎么說(shuō)呢?就是微服務(wù)之間的調(diào)用是使用的 RestTemplate 。比如這個(gè)時(shí)候我們 消費(fèi)者B 需要調(diào)用 提供者A 所提供的服務(wù)我們就需要這么寫(xiě)。如我下面的偽代碼。

@Autowired
private RestTemplate restTemplate;
// 這里是提供者A的ip地址,但是如果使用了 Eureka 那么就應(yīng)該是提供者A的名稱
private static final String SERVICE_PROVIDER_A = "http://localhost:8081";

@PostMapping("/judge")
public boolean judge(@RequestBody Request request) {
 String url = SERVICE_PROVIDER_A + "/service1";
 return restTemplate.postForObject(url, request, Boolean.class);
}

如果你對(duì)源碼感興趣的話,你會(huì)發(fā)現(xiàn)上面我們所講的 Eureka 框架中的 注冊(cè)、續(xù)約等,底層都是使用的 RestTemplate 。

為什么需要 Ribbon?
Ribbon 是 Netflix 公司的一個(gè)開(kāi)源的負(fù)載均衡 項(xiàng)目,是一個(gè)客戶端/進(jìn)程內(nèi)負(fù)載均衡器,運(yùn)行在消費(fèi)者端。

我們?cè)倥e個(gè),比如我們?cè)O(shè)計(jì)了一個(gè)秒殺系統(tǒng),但是為了整個(gè)系統(tǒng)的 高可用 ,我們需要將這個(gè)系統(tǒng)做一個(gè)集群,而這個(gè)時(shí)候我們消費(fèi)者就可以擁有多個(gè)秒殺系統(tǒng)的調(diào)用途徑了,如下圖。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

如果這個(gè)時(shí)候我們沒(méi)有進(jìn)行一些 均衡操作 ,如果我們對(duì) 秒殺系統(tǒng)1 進(jìn)行大量的調(diào)用,而另外兩個(gè)基本不請(qǐng)求,就會(huì)導(dǎo)致 秒殺系統(tǒng)1 崩潰,而另外兩個(gè)就變成了傀儡,那么我們?yōu)槭裁催€要做集群,我們高可用體現(xiàn)的意義又在哪呢?

所以 Ribbon 出現(xiàn)了,注意我們上面加粗的幾個(gè)字——運(yùn)行在消費(fèi)者端。指的是,Ribbon 是運(yùn)行在消費(fèi)者端的負(fù)載均衡器,如下圖。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

其工作原理就是 Consumer 端獲取到了所有的服務(wù)列表之后,在其內(nèi)部使用負(fù)載均衡算法,進(jìn)行對(duì)多個(gè)系統(tǒng)的調(diào)用。

Nginx 和 Ribbon 的對(duì)比
提到 負(fù)載均衡 就不得不提到大名鼎鼎的 Nignx 了,而和 Ribbon 不同的是,它是一種集中式的負(fù)載均衡器。

何為集中式呢?簡(jiǎn)單理解就是 將所有請(qǐng)求都集中起來(lái),然后再進(jìn)行負(fù)載均衡。如下圖。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

我們可以看到 Nginx 是接收了所有的請(qǐng)求進(jìn)行負(fù)載均衡的,而對(duì)于 Ribbon 來(lái)說(shuō)它是在消費(fèi)者端進(jìn)行的負(fù)載均衡。如下圖。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

請(qǐng)注意 Request 的位置,在 Nginx 中請(qǐng)求是先進(jìn)入負(fù)載均衡器,而在 Ribbon中是先在客戶端進(jìn)行負(fù)載均衡才進(jìn)行請(qǐng)求的。

Ribbon 的幾種負(fù)載均衡算法
負(fù)載均衡,不管 Nginx 還是 Ribbon 都需要其算法的支持,如果我沒(méi)記錯(cuò)的話 Nginx 使用的是 輪詢和加權(quán)輪詢算法。而在 Ribbon 中有更多的負(fù)載均衡調(diào)度算法,其默認(rèn)是使用的 RoundRobinRule 輪詢策略。

RoundRobinRule:輪詢策略。Ribbon 默認(rèn)采用的策略。若經(jīng)過(guò)一輪輪詢沒(méi)有找到可用的 provider,其最多輪詢 10 輪。若最終還沒(méi)有找到,則返回 null。
RandomRule: 隨機(jī)策略,從所有可用的 provider 中隨機(jī)選擇一個(gè)。
RetryRule: 重試策略。先按照 RoundRobinRule 策略獲取 provider,若獲取失敗,則在指定的時(shí)限內(nèi)重試。默認(rèn)的時(shí)限為 500 毫秒。
還有很多,這里不一一舉了,你最需要知道的是默認(rèn)輪詢算法,并且可以更換默認(rèn)的負(fù)載均衡算法,只需要在配置文件中做出修改就行。

providerName:
 ribbon:
 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

當(dāng)然,在 Ribbon 中你還可以自定義負(fù)載均衡算法,你只需要實(shí)現(xiàn) IRule 接口,然后修改配置文件或者自定義 Java Config 類。

什么是 Open Feign

有了 Eureka,RestTemplate,Ribbon 我們就可以愉快地進(jìn)行服務(wù)間的調(diào)用了,但是使用 RestTemplate 還是不方便,我們每次都要進(jìn)行這樣的調(diào)用。

@Autowired
private RestTemplate restTemplate;
// 這里是提供者A的ip地址,但是如果使用了 Eureka 那么就應(yīng)該是提供者A的名稱
private static final String SERVICE_PROVIDER_A = "http://localhost:8081";
@PostMapping("/judge")
public boolean judge(@RequestBody Request request) {
 String url = SERVICE_PROVIDER_A + "/service1";
 // 是不是太麻煩了???每次都要 url、請(qǐng)求、返回類型的
 return restTemplate.postForObject(url, request, Boolean.class);
}

這樣每次都調(diào)用 RestRemplate 的 API 是否太麻煩,我能不能像調(diào)用原來(lái)代碼一樣進(jìn)行各個(gè)服務(wù)間的調(diào)用呢?

聰明的小朋友肯定想到了,那就用 映射 呀,就像域名和IP地址的映射。我們可以將被調(diào)用的服務(wù)代碼映射到消費(fèi)者端,這樣我們就可以 “無(wú)縫開(kāi)發(fā)”啦。

OpenFeign 也是運(yùn)行在消費(fèi)者端的,使用 Ribbon 進(jìn)行負(fù)載均衡,所以 OpenFeign 直接內(nèi)置了 Ribbon。

在導(dǎo)入了 Open Feign 之后我們就可以進(jìn)行愉快編寫(xiě) Consumer 端代碼了。

// 使用 @FeignClient 注解來(lái)指定提供者的名字
@FeignClient(value = "eureka-client-provider")
public interface TestClient {
 // 這里一定要注意需要使用的是提供者那端的請(qǐng)求相對(duì)路徑,這里就相當(dāng)于映射了
 @RequestMapping(value = "/provider/xxx",
 method = RequestMethod.POST)
 CommonResponse<List<Plan>> getPlans(@RequestBody planGetRequest request);
}

然后我們?cè)?Controller 就可以像原來(lái)調(diào)用 Service 層代碼一樣調(diào)用它了。

@RestController
public class TestController {
 // 這里就相當(dāng)于原來(lái)自動(dòng)注入的 Service
 @Autowired
 private TestClient testClient;
 // controller 調(diào)用 service 層代碼
 @RequestMapping(value = "/test", method = RequestMethod.POST)
 public CommonResponse<List<Plan>> get(@RequestBody planGetRequest request) {
 return testClient.getPlans(request);
 }
}

必不可少的 Hystrix
什么是 Hystrix之熔斷和降級(jí)
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

在分布式環(huán)境中,不可避免地會(huì)有許多服務(wù)依賴項(xiàng)中的某些失敗。Hystrix是一個(gè)庫(kù),可通過(guò)添加等待時(shí)間容限和容錯(cuò)邏輯來(lái)幫助您控制這些分布式服務(wù)之間的交互。Hystrix通過(guò)隔離服務(wù)之間的訪問(wèn)點(diǎn),停止服務(wù)之間的級(jí)聯(lián)故障并提供后備選項(xiàng)來(lái)實(shí)現(xiàn)此目的,所有這些都可以提高系統(tǒng)的整體彈性。

總體來(lái)說(shuō) Hystrix 就是一個(gè)能進(jìn)行 熔斷 和 降級(jí) 的庫(kù),通過(guò)使用它能提高整個(gè)系統(tǒng)的彈性。

那么什么是 熔斷和降級(jí) 呢?再舉個(gè),此時(shí)我們整個(gè)微服務(wù)系統(tǒng)是這樣的。服務(wù)A調(diào)用了服務(wù)B,服務(wù)B再調(diào)用了服務(wù)C,但是因?yàn)槟承┰颍?wù)C頂不住了,這個(gè)時(shí)候大量請(qǐng)求會(huì)在服務(wù)C阻塞。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

服務(wù)C阻塞了還好,畢竟只是一個(gè)系統(tǒng)崩潰了。但是請(qǐng)注意這個(gè)時(shí)候因?yàn)榉?wù)C不能返回響應(yīng),那么服務(wù)B調(diào)用服務(wù)C的的請(qǐng)求就會(huì)阻塞,同理服務(wù)B阻塞了,那么服務(wù)A也會(huì)阻塞崩潰。

請(qǐng)注意,為什么阻塞會(huì)崩潰。因?yàn)檫@些請(qǐng)求會(huì)消耗占用系統(tǒng)的線程、IO 等資源,消耗完你這個(gè)系統(tǒng)服務(wù)器不就崩了么。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

這就叫 服務(wù)雪崩。媽耶,上面兩個(gè) 熔斷 和 降級(jí) 你都沒(méi)給我解釋清楚,你現(xiàn)在又給我扯什么 服務(wù)雪崩 ?

別急,聽(tīng)我慢慢道來(lái)。

所謂 熔斷 就是服務(wù)雪崩的一種有效解決方案。當(dāng)指定時(shí)間窗內(nèi)的請(qǐng)求失敗率達(dá)到設(shè)定閾值時(shí),系統(tǒng)將通過(guò) 斷路器 直接將此請(qǐng)求鏈路斷開(kāi)。

也就是我們上面服務(wù)B調(diào)用服務(wù)C在指定時(shí)間窗內(nèi),調(diào)用的失敗率到達(dá)了一定的值,那么 Hystrix 則會(huì)自動(dòng)將 服務(wù)B與C 之間的請(qǐng)求都斷了,以免導(dǎo)致服務(wù)雪崩現(xiàn)象。

其實(shí)這里所講的 熔斷 就是指的 Hystrix 中的 斷路器模式 ,你可以使用簡(jiǎn)單的 @HystrixCommand 注解來(lái)標(biāo)注某個(gè)方法,這樣 Hystrix 就會(huì)使用 斷路器 來(lái)“包裝”這個(gè)方法,每當(dāng)調(diào)用時(shí)間超過(guò)指定時(shí)間時(shí)(默認(rèn)為1000ms),斷路器將會(huì)中斷對(duì)這個(gè)方法的調(diào)用。

當(dāng)然你可以對(duì)這個(gè)注解的很多屬性進(jìn)行設(shè)置,比如設(shè)置超時(shí)時(shí)間,像這樣。

@HystrixCommand(
 commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1200")}
)
public List<Xxx> getXxxx() {
 // ...省略代碼邏輯
}

但是,我查閱了一些博客,發(fā)現(xiàn)他們都將 熔斷 和 降級(jí) 的概念混淆了,以我的理解,降級(jí)是為了更好的用戶體驗(yàn),當(dāng)一個(gè)方法調(diào)用異常時(shí),通過(guò)執(zhí)行另一種代碼邏輯來(lái)給用戶友好的回復(fù)。這也就對(duì)應(yīng)著 Hystrix 的 后備處理 模式。你可以通過(guò)設(shè)置 fallbackMethod 來(lái)給一個(gè)方法設(shè)置備用的代碼邏輯。比如這個(gè)時(shí)候有一個(gè)熱點(diǎn)新聞出現(xiàn)了,我們會(huì)推薦給用戶查看詳情,然后用戶會(huì)通過(guò)id去查詢新聞的詳情,但是因?yàn)檫@條新聞太火了(比如最近什么*易對(duì)吧),大量用戶同時(shí)訪問(wèn)可能會(huì)導(dǎo)致系統(tǒng)崩潰,那么我們就進(jìn)行 服務(wù)降級(jí) ,一些請(qǐng)求會(huì)做一些降級(jí)處理比如當(dāng)前人數(shù)太多請(qǐng)稍后查看等等。

// 指定了后備方法調(diào)用
@HystrixCommand(fallbackMethod = "getHystrixNews")
@GetMapping("/get/news")
public News getNews(@PathVariable("id") int id) {
 // 調(diào)用新聞系統(tǒng)的獲取新聞api 代碼邏輯省略
}
//
public News getHystrixNews(@PathVariable("id") int id) {
 // 做服務(wù)降級(jí)
 // 返回當(dāng)前人數(shù)太多,請(qǐng)稍后查看
}

什么是Hystrix之其他
我在閱讀 《Spring微服務(wù)實(shí)戰(zhàn)》這本書(shū)的時(shí)候還接觸到了一個(gè)艙壁模式的概念。在不使用艙壁模式的情況下,服務(wù)A調(diào)用服務(wù)B,這種調(diào)用默認(rèn)的是使用同一批線程來(lái)執(zhí)行的,而在一個(gè)服務(wù)出現(xiàn)性能問(wèn)題的時(shí)候,就會(huì)出現(xiàn)所有線程被刷爆并等待處理工作,同時(shí)阻塞新請(qǐng)求,最終導(dǎo)致程序崩潰。而艙壁模式會(huì)將遠(yuǎn)程資源調(diào)用隔離在他們自己的線程池中,以便可以控制單個(gè)表現(xiàn)不佳的服務(wù),而不會(huì)使該程序崩潰。

具體其原理我推薦大家自己去了解一下,本篇文章中對(duì)艙壁模式不做過(guò)多解釋。當(dāng)然還有 Hystrix 儀表盤,它是用來(lái)實(shí)時(shí)監(jiān)控 Hystrix 的各項(xiàng)指標(biāo)信息的,這里我將這個(gè)問(wèn)題也拋出去,希望有不了解的可以自己去搜索一下。

微服務(wù)網(wǎng)關(guān)——Zuul
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

ZUUL 是從設(shè)備和 web 站點(diǎn)到 Netflix 流應(yīng)用后端的所有請(qǐng)求的前門。作為邊界服務(wù)應(yīng)用,ZUUL 是為了實(shí)現(xiàn)動(dòng)態(tài)路由、監(jiān)視、彈性和安全性而構(gòu)建的。它還具有根據(jù)情況將請(qǐng)求路由到多個(gè) Amazon Auto Scaling Groups(亞馬遜自動(dòng)縮放組,亞馬遜的一種云計(jì)算方式) 的能力

在上面我們學(xué)習(xí)了 Eureka 之后我們知道了 服務(wù)提供者 是 消費(fèi)者 通過(guò) Eureka Server 進(jìn)行訪問(wèn)的,即 Eureka Server 是 服務(wù)提供者 的統(tǒng)一入口。那么整個(gè)應(yīng)用中存在那么多 消費(fèi)者 需要用戶進(jìn)行調(diào)用,這個(gè)時(shí)候用戶該怎樣訪問(wèn)這些 消費(fèi)者工程 呢?當(dāng)然可以像之前那樣直接訪問(wèn)這些工程。但這種方式?jīng)]有統(tǒng)一的消費(fèi)者工程調(diào)用入口,不便于訪問(wèn)與管理,而 Zuul 就是這樣的一個(gè)對(duì)于 消費(fèi)者 的統(tǒng)一入口。

如果學(xué)過(guò)前端的肯定都知道 Router 吧,比如 Flutter 中的路由,Vue,React中的路由,用了 Zuul 你會(huì)發(fā)現(xiàn)在路由功能方面和前端配置路由基本是一個(gè)理。 我偶爾擼擼 Flutter。

大家對(duì)網(wǎng)關(guān)應(yīng)該很熟吧,簡(jiǎn)單來(lái)講網(wǎng)關(guān)是系統(tǒng)唯一對(duì)外的入口,介于客戶端與服務(wù)器端之間,用于對(duì)請(qǐng)求進(jìn)行鑒權(quán)、限流、 路由、監(jiān)控等功能。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

沒(méi)錯(cuò),網(wǎng)關(guān)有的功能,Zuul 基本都有。而 Zuul中最關(guān)鍵的就是 路由和過(guò)濾器 了,在官方文檔中 Zuul 的標(biāo)題就是

Router and Filter : Zuul

Zuul 的路由功能
簡(jiǎn)單配置
本來(lái)想給你們復(fù)制一些代碼,但是想了想,因?yàn)楦鱾€(gè)代碼配置比較零散,看起來(lái)也比較零散,我決定還是給你們畫(huà)個(gè)圖來(lái)解釋吧。

請(qǐng)不要因?yàn)槲疫@么好就給我點(diǎn)贊 。瘋狂暗示。

比如這個(gè)時(shí)候我們已經(jīng)向 Eureka Server 注冊(cè)了兩個(gè) Consumer 、三個(gè) Provicer ,這個(gè)時(shí)候我們?cè)偌觽€(gè) Zuul 網(wǎng)關(guān)應(yīng)該變成這樣子了。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

emmm,信息量有點(diǎn)大,我來(lái)解釋一下。關(guān)于前面的知識(shí)我就不解釋了 。

首先,Zuul 需要向 Eureka 進(jìn)行注冊(cè),注冊(cè)有啥好處呢?

你傻呀,Consumer 都向 Eureka Server 進(jìn)行注冊(cè)了,我網(wǎng)關(guān)是不是只要注冊(cè)就能拿到所有 Consumer 的信息了?

拿到信息有什么好處呢?

我拿到信息我是不是可以獲取所有的 Consumer 的元數(shù)據(jù)(名稱,ip,端口)?

拿到這些元數(shù)據(jù)有什么好處呢?拿到了我們是不是直接可以做路由映射?比如原來(lái)用戶調(diào)用 Consumer1 的接口 localhost:8001/studentInfo/update 這個(gè)請(qǐng)求,我們是不是可以這樣進(jìn)行調(diào)用了呢?localhost:9000/consumer1/studentInfo/update 呢?你這樣是不是恍然大悟了?

這里的url為了讓更多人看懂所以沒(méi)有使用 restful 風(fēng)格。

上面的你理解了,那么就能理解關(guān)于 Zuul 最基本的配置了,看下面。

server:
 port: 9000
eureka:
 client:
 service-url:
 # 這里只要注冊(cè) Eureka 就行了
 defaultZone: http://localhost:9997/eureka

然后在啟動(dòng)類上加入 @EnableZuulProxy 注解就行了。沒(méi)錯(cuò),就是那么簡(jiǎn)單。

統(tǒng)一前綴
這個(gè)很簡(jiǎn)單,就是我們可以在前面加一個(gè)統(tǒng)一的前綴,比如我們剛剛調(diào)用的是 localhost:9000/consumer1/studentInfo/update,這個(gè)時(shí)候我們?cè)?yaml 配置文件中添加如下。

zuul:
 prefix: /zuul

這樣我們就需要通過(guò) localhost:9000/zuul/consumer1/studentInfo/update 來(lái)進(jìn)行訪問(wèn)了。

路由策略配置
你會(huì)發(fā)現(xiàn)前面的訪問(wèn)方式(直接使用服務(wù)名),需要將微服務(wù)名稱暴露給用戶,會(huì)存在安全性問(wèn)題。所以,可以自定義路徑來(lái)替代微服務(wù)名稱,即自定義路由策略。

zuul:
 routes:
 consumer1: /FrancisQ1/**
 consumer2: /FrancisQ2/**

這個(gè)時(shí)候你就可以使用 localhost:9000/zuul/FrancisQ1/studentInfo/update 進(jìn)行訪問(wèn)了。

服務(wù)名屏蔽
這個(gè)時(shí)候你別以為你好了,你可以試試,在你配置完路由策略之后使用微服務(wù)名稱還是可以訪問(wèn)的,這個(gè)時(shí)候你需要將服務(wù)名屏蔽。

zuul:
 ignore-services: "*"

路徑屏蔽
Zuul 還可以指定屏蔽掉的路徑 URI,即只要用戶請(qǐng)求中包含指定的 URI 路徑,那么該請(qǐng)求將無(wú)法訪問(wèn)到指定的服務(wù)。通過(guò)該方式可以限制用戶的權(quán)限。

zuul:
 ignore-patterns: **/auto/**

這樣關(guān)于 auto 的請(qǐng)求我們就可以過(guò)濾掉了。

* 代表匹配多級(jí)任意路徑
代表匹配一級(jí)任意路徑

敏感請(qǐng)求頭屏蔽
默認(rèn)情況下,像 Cookie、Set-Cookie 等敏感請(qǐng)求頭信息會(huì)被 zuul 屏蔽掉,我們可以將這些默認(rèn)屏蔽去掉,當(dāng)然,也可以添加要屏蔽的請(qǐng)求頭。

Zuul 的過(guò)濾功能
如果說(shuō),路由功能是 Zuul 的基操的話,那么過(guò)濾器就是 Zuul的利器了。畢竟所有請(qǐng)求都經(jīng)過(guò)網(wǎng)關(guān)(Zuul),那么我們可以進(jìn)行各種過(guò)濾,這樣我們就能實(shí)現(xiàn) 限流,灰度發(fā)布,權(quán)限控制 等等。

簡(jiǎn)單實(shí)現(xiàn)一個(gè)請(qǐng)求時(shí)間日志打印
要實(shí)現(xiàn)自己定義的 Filter 我們只需要繼承 ZuulFilter 然后將這個(gè)過(guò)濾器類以 @Component 注解加入 Spring 容器中就行了。

在給你們看代碼之前我先給你們解釋一下關(guān)于過(guò)濾器的一些注意點(diǎn)。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

過(guò)濾器類型:Pre、Routing、Post。前置Pre就是在請(qǐng)求之前進(jìn)行過(guò)濾,Routing路由過(guò)濾器就是我們上面所講的路由策略,而Post后置過(guò)濾器就是在 Response 之前進(jìn)行過(guò)濾的過(guò)濾器。你可以觀察上圖結(jié)合著理解,并且下面我會(huì)給出相應(yīng)的注釋。.

// 加入Spring容器
@Component
public class PreRequestFilter extends ZuulFilter {
 // 返回過(guò)濾器類型 這里是前置過(guò)濾器
 @Override
 public String filterType() {
 return FilterConstants.PRE_TYPE;
 }
 // 指定過(guò)濾順序 越小越先執(zhí)行,這里第一個(gè)執(zhí)行
 // 當(dāng)然不是只真正第一個(gè) 在Zuul內(nèi)置中有其他過(guò)濾器會(huì)先執(zhí)行
 // 那是寫(xiě)死的 比如 SERVLET_DETECTION_FILTER_ORDER = -3
 @Override
 public int filterOrder() {
 return 0;
 }
 // 什么時(shí)候該進(jìn)行過(guò)濾
 // 這里我們可以進(jìn)行一些判斷,這樣我們就可以過(guò)濾掉一些不符合規(guī)定的請(qǐng)求等等
 @Override
 public boolean shouldFilter() {
 return true;
 }
 // 如果過(guò)濾器允許通過(guò)則怎么進(jìn)行處理
 @Override
 public Object run() throws ZuulException {
 // 這里我設(shè)置了全局的RequestContext并記錄了請(qǐng)求開(kāi)始時(shí)間
 RequestContext ctx = RequestContext.getCurrentContext();
 ctx.set("startTime", System.currentTimeMillis());
 return null;
 }
}
// lombok的日志
@Slf4j
// 加入 Spring 容器
@Component
public class AccessLogFilter extends ZuulFilter {
 // 指定該過(guò)濾器的過(guò)濾類型
 // 此時(shí)是后置過(guò)濾器
 @Override
 public String filterType() {
 return FilterConstants.POST_TYPE;
 }
 // SEND_RESPONSE_FILTER_ORDER 是最后一個(gè)過(guò)濾器
 // 我們此過(guò)濾器在它之前執(zhí)行
 @Override
 public int filterOrder() {
 return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
 }
 @Override
 public boolean shouldFilter() {
 return true;
 }
 // 過(guò)濾時(shí)執(zhí)行的策略
 @Override
 public Object run() throws ZuulException {
 RequestContext context = RequestContext.getCurrentContext();
 HttpServletRequest request = context.getRequest();
 // 從RequestContext獲取原先的開(kāi)始時(shí)間 并通過(guò)它計(jì)算整個(gè)時(shí)間間隔
 Long startTime = (Long) context.get("startTime");
 // 這里我可以獲取HttpServletRequest來(lái)獲取URI并且打印出來(lái)
 String uri = request.getRequestURI();
 long duration = System.currentTimeMillis() - startTime;
 log.info("uri: " + uri + ", duration: " + duration / 100 + "ms");
 return null;
 }
}

上面就簡(jiǎn)單實(shí)現(xiàn)了請(qǐng)求時(shí)間日志打印功能,你有沒(méi)有感受到 Zuul 過(guò)濾功能的強(qiáng)大了呢?

沒(méi)有?好的、那我們?cè)賮?lái)。

令牌桶限流
當(dāng)然不僅僅是令牌桶限流方式,Zuul 只要是限流的活它都能干,這里我只是簡(jiǎn)單舉個(gè)。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

我先來(lái)解釋一下什么是 令牌桶限流 吧。

首先我們會(huì)有個(gè)桶,如果里面沒(méi)有滿那么就會(huì)以一定 固定的速率 會(huì)往里面放令牌,一個(gè)請(qǐng)求過(guò)來(lái)首先要從桶中獲取令牌,如果沒(méi)有獲取到,那么這個(gè)請(qǐng)求就拒絕,如果獲取到那么就放行。很簡(jiǎn)單吧,啊哈哈、

下面我們就通過(guò) Zuul 的前置過(guò)濾器來(lái)實(shí)現(xiàn)一下令牌桶限流。

@Component
@Slf4j
public class RouteFilter extends ZuulFilter {
 // 定義一個(gè)令牌桶,每秒產(chǎn)生2個(gè)令牌,即每秒最多處理2個(gè)請(qǐng)求
 private static final RateLimiter RATE_LIMITER = RateLimiter.create(2);
 @Override
 public String filterType() {
 return FilterConstants.PRE_TYPE;
 }
 @Override
 public int filterOrder() {
 return -5;
 }

 @Override
 public Object run() throws ZuulException {
 log.info("放行");
 return null;
 }

 @Override
 public boolean shouldFilter() {
 RequestContext context = RequestContext.getCurrentContext();
 if(!RATE_LIMITER.tryAcquire()) {
 log.warn("訪問(wèn)量超載");
 // 指定當(dāng)前請(qǐng)求未通過(guò)過(guò)濾
 context.setSendZuulResponse(false);
 // 向客戶端返回響應(yīng)碼429,請(qǐng)求數(shù)量過(guò)多
 context.setResponseStatusCode(429);
 return false;
 }
 return true;
 }
}

這樣我們就能將請(qǐng)求數(shù)量控制在一秒兩個(gè),有沒(méi)有覺(jué)得很酷?

關(guān)于 Zuul 的其他
Zuul 的過(guò)濾器的功能肯定不止上面我所實(shí)現(xiàn)的兩種,它還可以實(shí)現(xiàn) 權(quán)限校驗(yàn),包括我上面提到的 灰度發(fā)布 等等。

當(dāng)然,Zuul 作為網(wǎng)關(guān)肯定也存在 單點(diǎn)問(wèn)題 ,如果我們要保證 Zuul 的高可用,我們就需要進(jìn)行 Zuul 的集群配置,這個(gè)時(shí)候可以借助額外的一些負(fù)載均衡器比如 Nginx 。

Spring Cloud配置管理——Config
為什么要使用進(jìn)行配置管理?
當(dāng)我們的微服務(wù)系統(tǒng)開(kāi)始慢慢地龐大起來(lái),那么多 Consumer 、Provider 、Eureka Server 、Zuul 系統(tǒng)都會(huì)持有自己的配置,這個(gè)時(shí)候我們?cè)陧?xiàng)目運(yùn)行的時(shí)候可能需要更改某些應(yīng)用的配置,如果我們不進(jìn)行配置的統(tǒng)一管理,我們只能去每個(gè)應(yīng)用下一個(gè)一個(gè)尋找配置文件然后修改配置文件再重啟應(yīng)用。

首先對(duì)于分布式系統(tǒng)而言我們就不應(yīng)該去每個(gè)應(yīng)用下去分別修改配置文件,再者對(duì)于重啟應(yīng)用來(lái)說(shuō),服務(wù)無(wú)法訪問(wèn)所以直接拋棄了可用性,這是我們更不愿見(jiàn)到的。

那么有沒(méi)有一種方法既能對(duì)配置文件統(tǒng)一地進(jìn)行管理,又能在項(xiàng)目運(yùn)行時(shí)動(dòng)態(tài)修改配置文件呢?

那就是我今天所要介紹的 Spring Cloud Config 。

能進(jìn)行配置管理的框架不止Spring Cloud Config一種,大家可以根據(jù)需求自己選擇(disconf,阿波羅等等)。而且對(duì)于Config 來(lái)說(shuō)有些地方實(shí)現(xiàn)的不是那么盡人意。

Config 是什么
Spring Cloud Config 為分布式系統(tǒng)中的外部化配置提供服務(wù)器和客戶端支持。使用 Config服務(wù)器,可以在中心位置管理所有環(huán)境中應(yīng)用程序的外部屬性。

簡(jiǎn)單來(lái)說(shuō),Spring Cloud Config 就是能將各個(gè) 應(yīng)用/系統(tǒng)/模塊 的配置文件存放到 統(tǒng)一的地方然后進(jìn)行管理(Git 或者 SVN)。

你想一下,我們的應(yīng)用是不是只有啟動(dòng)的時(shí)候才會(huì)進(jìn)行配置文件的加載,那么我們的 Spring Cloud Config 就暴露出一個(gè)接口給啟動(dòng)應(yīng)用來(lái)獲取它所想要的配置文件,應(yīng)用獲取到配置文件然后再進(jìn)行它的初始化工作。就如下圖。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

當(dāng)然這里你肯定還會(huì)有一個(gè)疑問(wèn),如果我在應(yīng)用運(yùn)行時(shí)去更改遠(yuǎn)程配置倉(cāng)庫(kù)(Git)中的對(duì)應(yīng)配置文件,那么依賴于這個(gè)配置文件的已啟動(dòng)的應(yīng)用會(huì)不會(huì)進(jìn)行其相應(yīng)配置的更改呢?

答案是不會(huì)的。

什么?那怎么進(jìn)行動(dòng)態(tài)修改配置文件呢?這不是出現(xiàn)了 配置漂移 嗎?你個(gè)渣男,你又騙我!

別急嘛,你可以使用 Webhooks ,這是 github 提供的功能,它能確保遠(yuǎn)程庫(kù)的配置文件更新后客戶端中的配置信息也得到更新。

噢噢,這還差不多。我去查查怎么用。

慢著,聽(tīng)我說(shuō)完,Webhooks 雖然能解決,但是你了解一下會(huì)發(fā)現(xiàn)它根本不適合用于生產(chǎn)環(huán)境,所以基本不會(huì)使用它的。

而一般我們會(huì)使用 Bus 消息總線 + Spring Cloud Config 進(jìn)行配置的動(dòng)態(tài)刷新。

引出 Spring Cloud Bus
用于將服務(wù)和服務(wù)實(shí)例與分布式消息系統(tǒng)鏈接在一起的事件總線。在集群中傳播狀態(tài)更改很有用(例如配置更改事件)。

你可以簡(jiǎn)單理解為 Spring Cloud Bus 的作用就是管理和廣播分布式系統(tǒng)中的消息,也就是消息引擎系統(tǒng)中的廣播模式。當(dāng)然作為 消息總線 的 Spring Cloud Bus 可以做很多事而不僅僅是客戶端的配置刷新功能。

而擁有了 Spring Cloud Bus 之后,我們只需要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單的請(qǐng)求,并且加上 @ResfreshScope 注解就能進(jìn)行配置的動(dòng)態(tài)修改了,下面我畫(huà)了張圖供你理解。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

總結(jié)
這篇文章中我?guī)Т蠹页醪搅私饬?Spring Cloud 的各個(gè)組件,他們有

Eureka 服務(wù)發(fā)現(xiàn)框架
Ribbon 進(jìn)程內(nèi)負(fù)載均衡器
Open Feign 服務(wù)調(diào)用映射
Hystrix 服務(wù)降級(jí)熔斷器
Zuul 微服務(wù)網(wǎng)關(guān)
Config 微服務(wù)統(tǒng)一配置中心
Bus 消息總線
如果你能這個(gè)時(shí)候能看懂下面那張圖,也就說(shuō)明了你已經(jīng)對(duì) Spring Cloud 微服務(wù)有了一定的架構(gòu)認(rèn)識(shí)。
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

如果覺(jué)得我寫(xiě)的還不錯(cuò),那就留下個(gè)贊吧!

附上我和我女朋友今天上午的聊天記錄:
冒著被開(kāi)除的風(fēng)險(xiǎn)也要給你們看的 Spring Cloud 入門總結(jié)

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI