Spring Cloud 入門總結(jié)

【在此有話說】Spring Cloud是一系列框架的有序集合跨扮。它利用 Spring Boot的開發(fā)便利性巧妙地簡化了分布式系統(tǒng)基礎(chǔ)設(shè)施的開發(fā)希坚,如服務(wù)發(fā)現(xiàn)注冊试幽、配置中心氏义、消息總線究西、負(fù)載均衡窗慎、斷路器、數(shù)據(jù)監(jiān)控等,都可以用 Spring

Boot 的開發(fā)風(fēng)格做到一鍵啟動和部署遮斥。

首先我給大家看一張圖峦失,如果大家對這張圖有些地方不太理解的話,我希望你們看完我這篇文章會恍然大悟术吗。

總體架構(gòu)

什么是Spring Cloud

?? 構(gòu)建分布式系統(tǒng)不需要復(fù)雜和容易出錯尉辑。Spring Cloud為最常見的分布式系統(tǒng)模式提供了一種簡單且易于接受的編程模型,幫助開發(fā)人員構(gòu)建有彈性的较屿、可靠的隧魄、協(xié)調(diào)的應(yīng)用程序。Spring Cloud 構(gòu)建于

??? Spring Boot 之上隘蝎,使得開發(fā)者很容易入手并快速應(yīng)用于生產(chǎn)中购啄。

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

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

Spring Cloud 的版本

?? 當(dāng)然這個只是個題外話解孙。

?? Spring Cloud的版本號并不是我們通常見的數(shù)字版本號,而是一些很奇怪的單詞务荆。這些單詞均為英國倫敦地鐵站的站名。同時根據(jù)字母表的順序來對應(yīng)版本時間順序穷遂,比如:最早的 Release 版本 Angel函匕,第二個 Release 版本 Brixton(英國地名),然后是 Camden蚪黑、Dalston盅惜、Edgware、Finchley忌穿、Greenwich抒寂、Hoxton。

Spring Cloud 的服務(wù)發(fā)現(xiàn)框架——Eureka

?? Eureka 是基于REST(代表性狀態(tài)轉(zhuǎn)移)的服務(wù)掠剑,主要在 AWS 云中用于定位服務(wù)屈芜,以實現(xiàn)負(fù)載均衡和中間層服務(wù)器的故障轉(zhuǎn)移。我們稱此服務(wù)為 Eureka服務(wù)器。Eureka 還帶有一個基于 Java 的客戶端組件 EurekaClient井佑,它使與服務(wù)的交互變得更加容易属铁。客戶端還具有一個內(nèi)置的負(fù)載平衡器躬翁,可以執(zhí)行基本的循環(huán)負(fù)載平衡焦蘑。在Netflix,更復(fù)雜的負(fù)載均衡器將 Eureka 包裝起來盒发,以基于流量例嘱,資源使用,錯誤條件等多種因素提供加權(quán)負(fù)載均衡宁舰,以提供出色的彈性拼卵。

?? 總的來說,Eureka 就是一個服務(wù)發(fā)現(xiàn)框架明吩。何為服務(wù)间学,何又為發(fā)現(xiàn)呢?

?? 舉一個生活中的例子印荔,就比如我們平時租房子找中介的事情低葫。

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

?? 但是如果只是租客和房東之間進(jìn)行尋找的話,他們的效率是很低的炕横,房東找不到租客賺不到錢源内,租客找不到房東住不了房。所以份殿,后來房東肯定就想到了廣播自己的房源信息(比如在街邊貼貼小廣告)膜钓,這樣對于房東來說已經(jīng)完成他的任務(wù)(將房源公布出去),但是有兩個問題就出現(xiàn)了卿嘲。第一颂斜、其他不是租客的都能收到這種租房消息,這在現(xiàn)實世界沒什么拾枣,但是在計算機的世界中就會出現(xiàn)資源消耗的問題了沃疮。第二盒让、租客這樣還是很難找到你,試想一下我需要租房忿磅,我還需要東一個西一個地去找街邊小廣告糯彬,麻不麻煩?

?? 那怎么辦呢葱她?我們當(dāng)然不會那么傻乎乎的撩扒,第一時間就是去找 中介 呀,它為我們提供了統(tǒng)一房源的地方吨些,我們消費者只需要跑到它那里去找就行了搓谆。而對于房東來說,他們也只需要把房源在中介那里發(fā)布就行了豪墅。

?? 那么現(xiàn)在泉手,我們的模式就是這樣的了:

?? 但是,這個時候還會出現(xiàn)一些問題:

1.? 房東注冊之后如果不想賣房子了怎么辦偶器?我們是不是需要讓房東定期續(xù)約斩萌?如果房東不進(jìn)行續(xù)約是不是要將他們從中介那里的注冊列表中移除。

2.? 租客是不是也要進(jìn)行注冊呢屏轰?不然合同乙方怎么來呢颊郎?

3.? 中介可不可以做連鎖店呢?如果這一個店因為某些不可抗力因素而無法使用霎苗,那么我們是否可以換一個連鎖店呢姆吭?

針對上面的問題我們來重新構(gòu)建一下上面的模式圖:

?? 好了,舉完這個例子我們就可以來看關(guān)于 Eureka 的一些基礎(chǔ)概念了唁盏,你會發(fā)現(xiàn)這東西理解起來怎么這么簡單内狸。

?? 服務(wù)發(fā)現(xiàn):其實就是一個“中介”,整個過程中有三個角色:服務(wù)提供者(出租房子的)厘擂、服務(wù)消費者(租客)昆淡、服務(wù)中介(房屋中介)。

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

?? 服務(wù)消費者: 就是需要使用一些服務(wù)的“用戶”昂灵。

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

服務(wù)注冊 Register:

?? 官方解釋:當(dāng) Eureka 客戶端向 Eureka Server 注冊時恕曲,它提供自身的元數(shù)據(jù)鹏氧,比如 IP 地址、端口佩谣,運行狀況指示符 URL把还,主頁等。

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

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

?? 官方解釋:Eureka 客戶會每隔 30秒(默認(rèn)情況下)發(fā)送一次心跳來續(xù)約艇炎。 通過續(xù)約來告知 Eureka Server 該 Eureka 客戶仍然存在,沒有出現(xiàn)問題居砖。正常情況下驴娃,如果 Eureka Server 在 90 秒沒有收到 Eureka 客戶的續(xù)約奏候,它會將實例從其注冊表中刪除唇敞。

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

獲取注冊列表信息 Fetch Registries:

?? 官方解釋:Eureka客戶端從服務(wù)器獲取注冊表信息咒精,并將其緩存在本地狠轻”蚍福客戶端會使用該信息查找其他服務(wù)谐区,從而進(jìn)行遠(yuǎn)程調(diào)用。該注冊列表信息定期(每 30秒鐘)更新一次昭抒。每次返回注冊列表信息可能與 Eureka 客戶端的緩存信息不同灭返,Eureka客戶端自動處理熙含。如果由于某種原因?qū)е伦粤斜硇畔⒉荒芗皶r匹配艇纺,Eureka 客戶端則會重新獲取整個注冊表信息。 Eureka服務(wù)器緩存注冊列表信息腌乡,整個注冊表以及每個應(yīng)用程序的信息進(jìn)行了壓縮夜牡,壓縮內(nèi)容和沒有壓縮的內(nèi)容完全相同塘装。Eureka 客戶端和 Eureka服務(wù)器可以使用 JSON / XML 格式進(jìn)行通訊氢哮。在默認(rèn)的情況下 Eureka 客戶端使用壓縮 JSON 格式來獲取注冊列表的信息。

?? 結(jié)合中介理解:租客(消費者 Eureka ClientConsumer)去中介(服務(wù)器 Eureka Server)那里獲取所有的房屋信息列表(客戶端列表 Eureka ClientList)听盖,而且租客為了獲取最新的信息會定期向中介(服務(wù)器 Eureka Server)那里獲取并更新本地列表皆看。

服務(wù)下線 Cancel:

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

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

服務(wù)剔除 Eviction:

?? 官方解釋:在默認(rèn)的情況下昵观,當(dāng) Eureka 客戶端連續(xù) 90 秒(3個續(xù)約周期)沒有向Eureka服務(wù)器發(fā)送服務(wù)續(xù)約啊犬,即心跳觉至,Eureka 服務(wù)器會將該服務(wù)實例從服務(wù)注冊列表刪除,即服務(wù)剔除峻贮。

?? 結(jié)合中介理解:房東(提供者 Eureka ClientProvider)會定期聯(lián)系中介 (服務(wù)器 Eureka Server)告訴他我的房子還租(續(xù)約)纤控,如果中介(服務(wù)器 EurekaServer)長時間沒收到提供者的信息船万,那么中介會將他的房屋信息給下架(服務(wù)剔除)耿导。

?? 下面就是 Netflix 官方給出的 Eureka 架構(gòu)圖舱呻,你會發(fā)現(xiàn)和我們前面畫的中介圖別無二致箱吕。

Eureka架構(gòu)圖

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

負(fù)載均衡之 Ribbon

什么是 RestTemplate?

?? 不是講 Ribbon 么沥寥?怎么扯到了 RestTemplate 了柠座?你先別急妈经,聽我慢慢道來吹泡。

?? 我就說一句爆哑!RestTemplate 是 Spring

?? 提供的一個訪問 Http 服務(wù)的客戶端類揭朝,怎么說呢萝勤?就是微服務(wù)之間的調(diào)用是使用的 RestTemplate敌卓。比如這個時候我們消費者 B

?? 需要調(diào)用提供者 A 所提供的服務(wù)我們就需要這么寫趟径。如我下面的偽代碼:

?@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);

}

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

為什么需要 Ribbon医咨?

?? Ribbon 是 Netflix 公司的一個開源的負(fù)載均衡項目拟淮,是一個客戶端/進(jìn)程內(nèi)負(fù)載均衡器惩歉,運行在消費者端撑蚌。

?? 我們再舉個例子争涌,比如我們設(shè)計了一個秒殺系統(tǒng)亮垫,但是為了整個系統(tǒng)的高可用饮潦,我們需要將這個系統(tǒng)做一個集群继蜡,而這個時候我們消費者就可以擁有多個秒殺系統(tǒng)的調(diào)用途徑了稀并,如下圖:

?? 如果這個時候我們沒有進(jìn)行一些均衡操作碘举,如果我們對秒殺系統(tǒng) 1 進(jìn)行大量的調(diào)用引颈,而另外兩個基本不請求线欲,就會導(dǎo)致秒殺系統(tǒng) 1 崩潰李丰,而另外兩個就變成了傀儡趴泌,那么我們?yōu)槭裁催€要做集群嗜憔,我們高可用體現(xiàn)的意義又在哪呢吉捶?

?? 所以 Ribbon 出現(xiàn)了呐舔,注意我們上面加粗的幾個字——運行在消費者端珊拼。指的是澎现,Ribbon 是運行在消費者端的負(fù)載均衡器剑辫,如下圖:

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

Nginx 和 Ribbon 的對比

?? 提到負(fù)載均衡就不得不提到大名鼎鼎的 Nignx 了成艘,而和 Ribbon 不同的是,它是一種集中式的負(fù)載均衡器断箫。

?? 何為集中式呢?簡單理解就是 將所有請求都集中起來埃撵,然后再進(jìn)行負(fù)載均衡暂刘。如下圖:

?? 我們可以看到 Nginx 是接收了所有的請求進(jìn)行負(fù)載均衡的谣拣,而對于 Ribbon 來說它是在消費者端進(jìn)行的負(fù)載均衡森缠。如下圖:

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

Ribbon 的幾種負(fù)載均衡算法

?? 負(fù)載均衡坟奥,不管 Nginx 還是 Ribbon 都需要其算法的支持爱谁,如果我沒記錯的話 Nginx 使用的是 輪詢和加權(quán)輪詢算法访敌。而在 Ribbon 中有更多的負(fù)載均衡調(diào)度算法寺旺,其默認(rèn)是使用的 RoundRobinRule 輪詢策略阻塑。

1.? RoundRobinRule:輪詢策略陈莽。Ribbon 默認(rèn)采用的策略传透。若經(jīng)過一輪輪詢沒有找到可用的 provider朱盐,其最多輪詢 10 輪兵琳。若最終還沒有找到躯肌,則返回 null钱烟。

2.? RandomRule:隨機策略拴袭,從所有可用的 provider 中隨機選擇一個拥刻。

3.? RetryRule:重試策略般哼。先按照 RoundRobinRule 策略獲取 provider蒸眠,若獲取失敗楞卡,則在指定的時限內(nèi)重試臀晃。默認(rèn)的時限為 500 毫秒。

?? 還有很多座韵,這里不一一舉例子了宦棺,你最需要知道的是默認(rèn)輪詢算法代咸,并且可以更換默認(rèn)的負(fù)載均衡算法呐芥,只需要在配置文件中做出修改就行思瘟。

providerName:

ribbon:

NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

當(dāng)然够话,在 Ribbon 中你還可以自定義負(fù)載均衡算法女嘲,你只需要實現(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葵诈、請求作喘、返回類型的

return restTemplate.postForObject(url, request, Boolean.class);

}

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

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

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

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

// 使用 @FeignClient 注解來指定提供者的名字

@FeignClient(value = "eureka-client-provider")

public interface TestClient {

// 這里一定要注意需要使用的是提供者那端的請求相對路徑芯丧,這里就相當(dāng)于映射了

@RequestMapping(value = "/provider/xxx",

method = RequestMethod.POST)

CommonResponse<List<Plan>> getPlans(@RequestBody planGetRequest request);

}

然后我們在 Controller 就可以像原來調(diào)用 Service 層代碼一樣調(diào)用它了。

@RestController

public classTestController{

// 這里就相當(dāng)于原來自動注入的 Service

@Autowired

private TestClient testClient;

// controller 調(diào)用 service 層代碼

@RequestMapping(value ="/test", method = RequestMethod.POST)

return testClient.getPlans(request);

}

}

必不可少的 Hystrix

什么是 Hystrix之熔斷和降級缨恒?

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

?? 總體來說 Hystrix 就是一個能進(jìn)行熔斷和降級的庫述寡,通過使用它能提高整個系統(tǒng)的彈性柿隙。

?? 那么什么是熔斷和降級呢?再舉個例子鲫凶,此時我們整個微服務(wù)系統(tǒng)是這樣的禀崖。服務(wù) A 調(diào)用了服務(wù) B,服務(wù) B 再調(diào)用了服務(wù) C螟炫,但是因為某些原因波附,服務(wù) C 頂不住了,這個時候大量請求會在服務(wù) C 阻塞昼钻。

?? 服務(wù) C 阻塞了還好掸屡,畢竟只是一個系統(tǒng)崩潰了。但是請注意這個時候因為服務(wù) C 不能返回響應(yīng)换吧,那么服務(wù) B 調(diào)用服務(wù) C 的的請求就會阻塞折晦,同理服務(wù) B 阻塞了钥星,那么服務(wù) A 也會阻塞崩潰沾瓦。

?? 請注意,為什么阻塞會崩潰谦炒。因為這些請求會消耗占用系統(tǒng)的線程贯莺、IO 等資源,消耗完你這個系統(tǒng)服務(wù)器不就崩了么宁改。

?? 這就叫服務(wù)雪崩缕探。媽耶,上面兩個熔斷和降級你都沒給我解釋清楚还蹲,你現(xiàn)在又給我扯什么服務(wù)雪崩爹耗?

?? 別急耙考,聽我慢慢道來。

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

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

?? 其實這里所講的 熔斷 就是指的 Hystrix 中的斷路器模式 枚碗,你可以使用簡單的 @HystrixCommand 注解來標(biāo)注某個方法,這樣 Hystrix 就會使用 斷路器來“包裝”這個方法铸本,每當(dāng)調(diào)用時間超過指定時間時(默認(rèn)為1000ms)肮雨,斷路器將會中斷對這個方法的調(diào)用。

?? 當(dāng)然你可以對這個注解的很多屬性進(jìn)行設(shè)置箱玷,比如設(shè)置超時時間酷含,像這樣:

@HystrixCommand(

commandProperties = {@HystrixProperty(name = "execution.isolation.thread.tim

eoutInMilliseconds",value = "1200")}

)

public List<Xxx> getXxxx() {

// ...省略代碼邏輯

}

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

// 指定了后備方法調(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ù)降級

// 返回當(dāng)前人數(shù)太多顾患,請稍后查看

}

?么是Hystrix之其他

?? 我在閱讀《Spring微服務(wù)實戰(zhàn)》這本書的時候還接觸到了一個艙壁模式的概念番捂。在不使用艙壁模式的情況下,服務(wù)A 調(diào)用服務(wù)B江解,這種調(diào)用默認(rèn)的是使用同一批線程來執(zhí)行的设预,而在一個服務(wù)出現(xiàn)性能問題的時候,就會出現(xiàn)所有線程被刷爆并等待處理工作犁河,同時阻塞新請求鳖枕,最終導(dǎo)致程序崩潰魄梯。而艙壁模式會將遠(yuǎn)程資源調(diào)用隔離在他們自己的線程池中,以便可以控制單個表現(xiàn)不佳的服務(wù)宾符,而不會使該程序崩潰画恰。

具體其原理我推薦大家自己去了解一下,本篇文章中對艙壁模式不做過多解釋吸奴。當(dāng)然還有 Hystrix 儀表盤允扇,它是用來實時監(jiān)控 Hystrix 的各項指標(biāo)信息的,這里我將這個問題也拋出去则奥,希望有不了解的可以自己去搜索一下考润。

微服務(wù)網(wǎng)關(guān)——Zuul

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

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

?? 如果學(xué)過前端的肯定都知道 Router 吧,比如 Flutter 中的路由糯而,Vue天通,React中的路由,用了 Zuul 你會發(fā)現(xiàn)在路由功能方面和前端配置路由基本是一個理熄驼。

?? 大家對網(wǎng)關(guān)應(yīng)該很熟吧像寒,簡單來講網(wǎng)關(guān)是系統(tǒng)唯一對外的入口,介于客戶端與服務(wù)器端之間谜洽,用于對請求進(jìn)行鑒權(quán)萝映、限流、 路由阐虚、監(jiān)控等功能。

沒錯蚌卤,網(wǎng)關(guān)有的功能实束,Zuul 基本都有奥秆。而 Zuul 中最關(guān)鍵的就是路由和過濾器了,在官方文檔中 Zuul 的標(biāo)題就是:

Router and Filter : Zuul

Zuul 的路由功能

?? 簡單配置

?? 本來想給你們復(fù)制一些代碼咸灿,但是想了想构订,因為各個代碼配置比較零散,看起來也比較零散避矢,我決定還是給你們畫個圖來解釋吧悼瘾。

?? 比如這個時候我們已經(jīng)向 Eureka Server 注冊了兩個 Consumer 、三個 Provicer 审胸,這個時候我們再加個 Zuul 網(wǎng)關(guān)應(yīng)該變成這樣子了:

?? 信息量有點大亥宿,我來解釋一下。關(guān)于前面的知識我就不解釋了 砂沛。

?? 首先烫扼,Zuul 需要向 Eureka 進(jìn)行注冊,注冊有啥好處呢碍庵?

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

?? 拿到信息有什么好處呢堰氓?

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

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

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

?? 上面的你理解了哪怔,那么就能理解關(guān)于 Zuul 最基本的配置了宣蔚,看下面:

server:

port: 9000

eureka:

client:

# 這里只要注冊 Eureka 就行了

defaultZone: http://localhost:9997/eureka

?? 然后在啟動類上加入 @EnableZuulProxy 注解就行了。沒錯认境,就是那么簡單胚委。

?? 統(tǒng)一前綴

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

zuul:

prefix: /zuul

?? 這樣我們就需要通過 localhost:9000/zuul/consumer1/studentInfo/update 來進(jìn)行訪問了。

?? 路由策略配置

?? 你會發(fā)現(xiàn)前面的訪問方式(直接使用服務(wù)名)硅急,需要將微服務(wù)名稱暴露給用戶覆享,會存在安全性問題。所以营袜,可以自定義路徑來替代微服務(wù)名稱撒顿,即自定義路由策略。

zuul:

routes:?

consumer1: /FrancisQ1/**

consumer2: /FrancisQ2/**

?? 這個時候你就可以使用 localhost:9000/zuul/FrancisQ1/studentInfo/update 進(jìn)行訪問了荚板。

服務(wù)名屏蔽

?? 這個時候你別以為你好了凤壁,你可以試試,在你配置完路由策略之后使用微服務(wù)名稱還是可以訪問的跪另,這個時候你需要將服務(wù)名屏蔽拧抖。

zuul:

ignore-services: "*"

?? 路徑屏蔽

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

zuul:

ignore-patterns:**/auto/**

?? 這樣關(guān)于 auto 的請求我們就可以過濾掉了针姿。

?? ** 代表匹配多級任意路徑

?? *代表匹配一級任意路徑

?? 敏感請求頭屏蔽

?? 默認(rèn)情況下袱吆,像 Cookie、Set-Cookie 等敏感請求頭信息會被 Zuul 屏蔽掉距淫,我們可以將這些默認(rèn)屏蔽去掉绞绒,當(dāng)然,也可以添加要屏蔽的請求頭榕暇。

Zuul 的過濾功能

?? 如果說蓬衡,路由功能是 Zuul 的基操的話,那么過濾器就是 Zuul 的利器了彤枢。畢竟所有請求都經(jīng)過網(wǎng)關(guān)(Zuul)狰晚,那么我們可以進(jìn)行各種過濾,這樣我們就能實現(xiàn) 限流缴啡,灰度發(fā)布壁晒,權(quán)限控制等等。

?? 簡單實現(xiàn)一個請求時間日志打印

?? 要實現(xiàn)自己定義的 Filter 我們只需要繼承 ZuulFilter 然后將這個過濾器類以 @Component 注解加入 Spring 容器中就行了业栅。

令牌桶限流

當(dāng)然不僅僅是令牌桶限流方式秒咐,Zuul 只要是限流的活它都能干,這里我只是簡單舉個例子碘裕。


我先來解釋一下什么是令牌桶限流吧携取。

?? 首先我們會有個桶,如果里面沒有滿那么就會以一定 固定的速率 會往里面放令牌帮孔,一個請求過來首先要從桶中獲取令牌雷滋,如果沒有獲取到,那么這個請求就拒絕,如果獲取到那么就放行惊豺。

下面我們就通過 Zuul 的前置過濾器來實現(xiàn)一下令牌桶限流燎孟。

@Component@Slf4jpublic classRouteFilterextendsZuulFilter{// 定義一個令牌桶禽作,每秒產(chǎn)生2個令牌尸昧,即每秒最多處理2個請求 private static final RateLimiter RATE_LIMITER = RateLimiter.create(2); @OverridepublicStringfilterType(){

? ? ? ? return FilterConstants.PRE_TYPE;? ?

}@Override publicintfilterOrder(){

? ? ? ? return -5;

}@Override publicObjectrun()throwsZuulException{

? ? ? ? log.info("放行");

? ? ? ? return null;? ? }@OverridepublicbooleanshouldFilter(){

? ? ? ? RequestContext context = RequestContext.getCurrentContext();? ? ? ?

? ? ? ? if(!RATE_LIMITER.tryAcquire()) {

? ? ? ? ? ? ? ? ? ? log.warn("訪問量超載");? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? // 指定當(dāng)前請求未通過過濾? ? ? ? ? ? ? ? ? ? context.setSendZuulResponse(false);

? ? ? ? ? ? ? ? ? ? // 向客戶端返回響應(yīng)碼429,請求數(shù)量過多? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? context.setResponseStatusCode(429);

? ? ? ? ? ? ? ? ? ? return false;

? ? ? ? ? }

? ? ? ? ? return true;

}

}

這樣我們就能將請求數(shù)量控制在一秒兩個旷偿,有沒有覺得很酷烹俗?

關(guān)于 Zuul 的其他

?? Zuul 的過濾器的功能肯定不止上面我所實現(xiàn)的兩種,它還可以實現(xiàn)權(quán)限校驗萍程,包括我上面提到的灰度發(fā)布等等幢妄。

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

Spring Cloud配置管理——Config

?? 為什么要使用進(jìn)行配置管理潮尝?

?? 當(dāng)我們的微服務(wù)系統(tǒng)開始慢慢地龐大起來,那么多 Consumer饿序、Provider勉失、Eureka Server、Zuul 系統(tǒng)都會持有自己的配置原探,這個時候我們在項目運行的時候可能需要更改某些應(yīng)用的配置乱凿,如果我們不進(jìn)行配置的統(tǒng)一管理,我們只能去每個應(yīng)用下一個一個尋找配置文件然后修改配置文件再重啟應(yīng)用咽弦。

?? 首先對于分布式系統(tǒng)而言我們就不應(yīng)該去每個應(yīng)用下去分別修改配置文件徒蟆,再者對于重啟應(yīng)用來說,服務(wù)無法訪問所以直接拋棄了可用性型型,這是我們更不愿見到的段审。

?? 那么有沒有一種方法既能對配置文件統(tǒng)一地進(jìn)行管理,又能在項目運行時動態(tài)修改配置文件呢输莺?

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

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

Config 是什么

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

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

?? 你想一下蓄氧,我們的應(yīng)用是不是只有啟動的時候才會進(jìn)行配置文件的加載,那么我們的 Spring Cloud Config 就暴露出一個接口給啟動應(yīng)用來獲取它所想要的配置文件槐脏,應(yīng)用獲取到配置文件然后再進(jìn)行它的初始化工作喉童。就如下圖:

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

?? 答案是不會的。

?? 什么牌废?那怎么進(jìn)行動態(tài)修改配置文件呢咽白?這不是出現(xiàn)了配置漂移嗎?你個渣男鸟缕,你又騙我晶框!

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

?? 噢噢莫绣,這還差不多畴蒲。我去查查怎么用。

?? 慢著对室,聽我說完模燥,Webhooks 雖然能解決,但是你了解一下會發(fā)現(xiàn)它根本不適合用于生產(chǎn)環(huán)境掩宜,所以基本不會使用它的蔫骂。

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

引出 Spring Cloud Bus

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

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

?? 而擁有了 Spring Cloud Bus 之后,我們只需要創(chuàng)建一個簡單的請求追迟,并且加上 @ResfreshScope 注解就能進(jìn)行配置的動態(tài)修改了溶其,下面我畫了張圖供你理解。

總結(jié)

這篇文章中我?guī)Т蠹页醪搅私饬?Spring Cloud 的各個組件敦间,他們有:

Eureka 服務(wù)發(fā)現(xiàn)框架

Ribbon 進(jìn)程內(nèi)負(fù)載均衡器

Open Feign 服務(wù)調(diào)用映射

Hystrix 服務(wù)降級熔斷器

Zuul 微服務(wù)網(wǎng)關(guān)

Config 微服務(wù)統(tǒng)一配置中心

Bus 消息總線

我自己建了個群瓶逃,對 JAVA 開發(fā)有興趣的朋友歡迎加入QQ群:322708204進(jìn)行技術(shù)討論束铭,里面資深架構(gòu)師會分享一些整理好的BATJ面試題:有Spring,MyBatis厢绝,Netty源碼分析契沫,高并發(fā)、高性能昔汉、分布式懈万、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化挤庇、分布式架構(gòu)等這些成為架構(gòu)師必備的知識體系钞速。

如果你能這個時候能看懂下面那張圖贷掖,也就說明了你已經(jīng)對 Spring Cloud 微服務(wù)有了一定的架構(gòu)認(rèn)識嫡秕。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苹威,隨后出現(xiàn)的幾起案子昆咽,更是在濱河造成了極大的恐慌,老刑警劉巖牙甫,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掷酗,死亡現(xiàn)場離奇詭異,居然都是意外死亡窟哺,警方通過查閱死者的電腦和手機泻轰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來且轨,“玉大人浮声,你說我怎么就攤上這事⌒荩” “怎么了泳挥?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長至朗。 經(jīng)常有香客問我屉符,道長,這世上最難降的妖魔是什么锹引? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任矗钟,我火速辦了婚禮,結(jié)果婚禮上嫌变,老公的妹妹穿的比我還像新娘吨艇。我一直安慰自己,他們只是感情好初澎,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布秸应。 她就那樣靜靜地躺著虑凛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪软啼。 梳的紋絲不亂的頭發(fā)上桑谍,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音祸挪,去河邊找鬼锣披。 笑死,一個胖子當(dāng)著我的面吹牛贿条,可吹牛的內(nèi)容都是我干的雹仿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼整以,長吁一口氣:“原來是場噩夢啊……” “哼胧辽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起公黑,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤邑商,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后凡蚜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體人断,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年朝蜘,在試婚紗的時候發(fā)現(xiàn)自己被綠了恶迈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谱醇,死狀恐怖暇仲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枣抱,我是刑警寧澤熔吗,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站佳晶,受9級特大地震影響桅狠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轿秧,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一中跌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧菇篡,春花似錦漩符、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凸克。三九已至,卻和暖如春闷沥,著一層夾襖步出監(jiān)牢的瞬間萎战,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工舆逃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚂维,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓路狮,卻偏偏與公主長得像虫啥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子奄妨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容