您當前的位置:首頁 > 書法

Spring Cloud 入門總結

作者:由 老劉 發表于 書法時間:2019-12-06

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

Spring Cloud 入門總結

總體架構

什麼是Spring cloud

構建分散式系統不需要複雜和容易出錯。Spring Cloud 為最常見的分散式系統模式提供了一種簡單且易於接受的程式設計模型,幫助開發人員構建有彈性的、可靠的、協調的應用程式。Spring Cloud 構建於 Spring Boot 之上,使得開發者很容易入手並快速應用於生產中。

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

我所理解的

Spring Cloud

就是微服務系統架構的一站式解決方案,在平時我們構建微服務的過程中需要做如

服務發現註冊

配置中心

訊息匯流排

負載均衡

斷路器

資料監控

等操作,而 Spring Cloud 為我們提供了一套簡易的程式設計模型,使我們能在 Spring Boot 的基礎上輕鬆地實現微服務專案的構建。

Spring Cloud 的版本

當然這個只是個題外話。

Spring Cloud 的版本號並不是我們通常見的數字版本號,而是一些很奇怪的單詞。這些單詞均為英國倫敦地鐵站的站名。同時根據字母表的順序來對應版本時間順序,比如:最早 的 Release 版本 Angel,第二個 Release 版本 Brixton(英國地名),然後是 Camden、 Dalston、Edgware、Finchley、Greenwich、Hoxton。

Spring Cloud 的服務發現框架——Eureka

Eureka是基於REST(代表性狀態轉移)的服務,主要在AWS雲中用於定位服務,以實現負載均衡和中間層伺服器的故障轉移。我們稱此服務為Eureka伺服器。Eureka還帶有一個基於Java的客戶端元件Eureka Client,它使與服務的互動變得更加容易。客戶端還具有一個內建的負載平衡器,可以執行基本的迴圈負載平衡。在Netflix,更復雜的負載均衡器將Eureka包裝起來,以基於流量,資源使用,錯誤條件等多種因素提供加權負載均衡,以提供出色的彈性。

總的來說,

Eureka

就是一個服務發現框架。何為服務,何又為發現呢?

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

在沒有中介的時候我們需要一個一個去尋找是否有房屋要出租的房東,這顯然會非常的費力,一你找憑一個人的能力是找不到很多房源供你選擇,再者你也懶得這麼找下去(找了這麼久,沒有合適的只能將就)。

這裡的我們就相當於微服務中的

Consumer

,而那些房東就相當於微服務中的

Provider

。消費者

Consumer

需要呼叫提供者

Provider

提供的一些服務,就像我們現在需要租他們的房子一樣。

但是如果只是租客和房東之間進行尋找的話,他們的效率是很低的,房東找不到租客賺不到錢,租客找不到房東住不了房。所以,後來房東肯定就想到了廣播自己的房源資訊(比如在街邊貼貼小廣告),這樣對於房東來說已經完成他的任務(將房源公佈出去),但是有兩個問題就出現了。第一、其他不是租客的都能收到這種租房訊息,這在現實世界沒什麼,但是在計算機的世界中就會出現

資源消耗

的問題了。第二、租客這樣還是很難找到你,試想一下我需要租房,我還需要東一個西一個地去找街邊小廣告,麻不麻煩?

Spring Cloud 入門總結

那怎麼辦呢?我們當然不會那麼傻乎乎的,第一時間就是去找

中介

呀,它為我們提供了統一房源的地方,我們消費者只需要跑到它那裡去找就行了。而對於房東來說,他們也只需要把房源在中介那裡釋出就行了。

Spring Cloud 入門總結

那麼現在,我們的模式就是這樣的了。

Spring Cloud 入門總結

但是,這個時候還會出現一些問題。

房東註冊之後如果不想賣房子了怎麼辦?我們是不是需要讓房東

定期續約

?如果房東不進行續約是不是要將他們從中介那裡的註冊列表中

移除

租客是不是也要進行

註冊

呢?不然合同乙方怎麼來呢?

中介可不可以做

連鎖店

呢?如果這一個店因為某些不可抗力因素而無法使用,那麼我們是否可以換一個連鎖店呢?

針對上面的問題我們來重新構建一下上面的模式圖

Spring Cloud 入門總結

好了,舉完這個 我們就可以來看關於

Eureka

的一些基礎概念了,你會發現這東西理解起來怎麼這麼簡單。

服務發現

:其實就是一個“中介”,整個過程中有三個角色:

服務提供者(出租房子的)、服務消費者(租客)、服務中介(房屋中介)

服務提供者

: 就是提供一些自己能夠執行的一些服務給外界。

服務消費者

: 就是需要使用一些服務的“使用者”。

服務中介

: 其實就是服務提供者和服務消費者之間的“橋樑”,服務提供者可以把自己註冊到服務中介那裡,而服務消費者如需要消費一些服務(使用一些功能)就可以在服務中介中尋找註冊在服務中介的服務提供者。

服務註冊 Register

官方解釋:當

Eureka

客戶端向

Eureka Server

註冊時,它提供自身的

元資料

,比如IP地址、埠,執行狀況指示符URL,主頁等。

結合中介理解:房東 (提供者

Eureka Client Provider

)在中介 (伺服器

Eureka Server

) 那裡登記房屋的資訊,比如面積,價格,地段等等(元資料

metaData

)。

服務續約 Renew

官方解釋:

Eureka

客戶會每隔30秒(預設情況下)傳送一次心跳來續約

。 透過續約來告知

Eureka Server

Eureka

客戶仍然存在,沒有出現問題。 正常情況下,如果

Eureka Server

在90秒沒有收到

Eureka

客戶的續約,它會將例項從其登錄檔中刪除。

結合中介理解:房東 (提供者

Eureka Client Provider

) 定期告訴中介 (伺服器

Eureka Server

) 我的房子還租(續約) ,中介 (伺服器

Eureka Server

) 收到之後繼續保留房屋的資訊。

獲取註冊列表資訊 Fetch Registries

官方解釋:

Eureka

客戶端從伺服器獲取登錄檔資訊,並將其快取在本地。客戶端會使用該資訊查詢其他服務,從而進行遠端呼叫。該註冊列表資訊定期(每30秒鐘)更新一次。每次返回註冊列表資訊可能與

Eureka

客戶端的快取資訊不同,

Eureka

客戶端自動處理。如果由於某種原因導致註冊列表資訊不能及時匹配,

Eureka

客戶端則會重新獲取整個登錄檔資訊。

Eureka

伺服器快取註冊列表資訊,整個登錄檔以及每個應用程式的資訊進行了壓縮,壓縮內容和沒有壓縮的內容完全相同。

Eureka

客戶端和

Eureka

伺服器可以使用JSON / XML格式進行通訊。在預設的情況下

Eureka

客戶端使用壓縮

JSON

格式來獲取註冊列表的資訊。

結合中介理解:租客(消費者

Eureka Client Consumer

) 去中介 (伺服器

Eureka Server

) 那裡獲取所有的房屋資訊列表 (客戶端列表

Eureka Client List

) ,而且租客為了獲取最新的資訊會定期向中介 (伺服器

Eureka Server

) 那裡獲取並更新本地列表。

服務下線 Cancel

官方解釋:Eureka客戶端在程式關閉時向Eureka伺服器傳送取消請求。 傳送請求後,該客戶端例項資訊將從伺服器的例項登錄檔中刪除。該下線請求不會自動完成,它需要呼叫以下內容:

DiscoveryManager。getInstance()。shutdownComponent();

結合中介理解:房東 (提供者

Eureka Client Provider

) 告訴中介 (伺服器

Eureka Server

) 我的房子不租了,中介之後就將註冊的房屋資訊從列表中剔除。

服務剔除 Eviction

官方解釋:在預設的情況下,

當Eureka客戶端連續90秒(3個續約週期)沒有向Eureka伺服器傳送服務續約,即心跳,Eureka伺服器會將該服務例項從服務註冊列表刪除

,即服務剔除。

結合中介理解:房東(提供者

Eureka Client Provider

) 會定期聯絡 中介 (伺服器

Eureka Server

) 告訴他我的房子還租(續約),如果中介 (伺服器

Eureka Server

) 長時間沒收到提供者的資訊,那麼中介會將他的房屋資訊給下架(服務剔除)。

下面就是

Netflix

官方給出的

Eureka

架構圖,你會發現和我們前面畫的中介圖別無二致。

Spring Cloud 入門總結

當然,可以充當服務發現的元件有很多:

Zookeeper

Consul

Eureka

等。

更多關於

Eureka

的知識(自我保護,初始註冊策略等等)可以自己去官網檢視,或者檢視這篇文章 深入理解 Eureka。

負載均衡之 Ribbon

什麼是 RestTemplate?

不是講

Ribbon

麼?怎麼扯到了

RestTemplate

了?你先別急,聽我慢慢道來。

我不聽我不聽我不聽 。

我就說一句!

RestTemplate是Spring提供的一個訪問Http服務的客戶端類

,怎麼說呢?就是微服務之間的呼叫是使用的

RestTemplate

。比如這個時候我們 消費者B 需要呼叫 提供者A 所提供的服務我們就需要這麼寫。如我下面的虛擬碼。

@Autowired

private

RestTemplate

restTemplate

// 這裡是提供者A的ip地址,但是如果使用了 Eureka 那麼就應該是提供者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

);

}

如果你對原始碼感興趣的話,你會發現上面我們所講的

Eureka

框架中的

註冊

續約

等,底層都是使用的

RestTemplate

為什麼需要 Ribbon?

Ribbon

Netflix

公司的一個開源的負載均衡 專案,是一個客戶端/程序內負載均衡器,

執行在消費者端

我們再舉個 ,比如我們設計了一個秒殺系統,但是為了整個系統的

高可用

,我們需要將這個系統做一個叢集,而這個時候我們消費者就可以擁有多個秒殺系統的呼叫途徑了,如下圖。

Spring Cloud 入門總結

如果這個時候我們沒有進行一些

均衡操作

,如果我們對

秒殺系統1

進行大量的呼叫,而另外兩個基本不請求,就會導致

秒殺系統1

崩潰,而另外兩個就變成了傀儡,那麼我們為什麼還要做叢集,我們高可用體現的意義又在哪呢?

所以

Ribbon

出現了,注意我們上面加粗的幾個字——

執行在消費者端

。指的是,

Ribbon

是執行在消費者端的負載均衡器,如下圖。

Spring Cloud 入門總結

其工作原理就是

Consumer

端獲取到了所有的服務列表之後,在其

內部

使用

負載均衡演算法

,進行對多個系統的呼叫。

Nginx 和 Ribbon 的對比

提到

負載均衡

就不得不提到大名鼎鼎的

Nignx

了,而和

Ribbon

不同的是,它是一種

集中式

的負載均衡器。

何為集中式呢?簡單理解就是

將所有請求都集中起來,然後再進行負載均衡

。如下圖。

Spring Cloud 入門總結

我們可以看到

Nginx

是接收了所有的請求進行負載均衡的,而對於

Ribbon

來說它是在消費者端進行的負載均衡。如下圖。

Spring Cloud 入門總結

請注意

Request

的位置,在

Nginx

中請求是先進入負載均衡器,而在

Ribbon

中是先在客戶端進行負載均衡才進行請求的。

Ribbon 的幾種負載均衡演算法

負載均衡,不管

Nginx

還是

Ribbon

都需要其演算法的支援,如果我沒記錯的話

Nginx

使用的是 輪詢和加權輪詢演算法。而在

Ribbon

中有更多的負載均衡排程演算法,其預設是使用的

RoundRobinRule

輪詢策略。

RoundRobinRule

:輪詢策略。

Ribbon

預設採用的策略。若經過一輪輪詢沒有找到可用的

provider

,其最多輪詢 10 輪。若最終還沒有找到,則返回 null。

RandomRule

: 隨機策略,從所有可用的 provider 中隨機選擇一個。

RetryRule

: 重試策略。先按照 RoundRobinRule 策略獲取 provider,若獲取失敗,則在指定的時限內重試。預設的時限為 500 毫秒。

還有很多,這裡不一一舉 了,你最需要知道的是預設輪詢演算法,並且可以更換預設的負載均衡演算法,只需要在配置檔案中做出修改就行。

providerName:

ribbon

NFLoadBalancerRuleClassName

com

netflix

loadbalancer

RandomRule

當然,在

Ribbon

中你還可以

自定義負載均衡演算法

,你只需要實現

IRule

介面,然後修改配置檔案或者自定義

Java Config

類。

什麼是 Open Feign

有了

Eureka

RestTemplate

Ribbon

我們就可以 愉快地進行服務間的呼叫了,但是使用

RestTemplate

還是不方便,我們每次都要進行這樣的呼叫。

@Autowired

private

RestTemplate

restTemplate

// 這裡是提供者A的ip地址,但是如果使用了 Eureka 那麼就應該是提供者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

);

}

這樣每次都呼叫

RestRemplate

API

是否太麻煩,我能不能像

呼叫原來程式碼一樣進行各個服務間的呼叫呢?

聰明的小朋友肯定想到了,那就用

對映

呀,就像域名和IP地址的對映。我們可以將被呼叫的服務程式碼對映到消費者端,這樣我們就可以

“無縫開發”

啦。

OpenFeign 也是執行在消費者端的,使用 Ribbon 進行負載均衡,所以 OpenFeign 直接內建了 Ribbon。

在匯入了

Open Feign

之後我們就可以進行愉快編寫

Consumer

端程式碼了。

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

@FeignClient

value

=

“eureka-client-provider”

public

interface

TestClient

{

// 這裡一定要注意需要使用的是提供者那端的請求相對路徑,這裡就相當於映射了

@RequestMapping

value

=

“/provider/xxx”

method

=

RequestMethod

POST

CommonResponse

<

List

<

Plan

>>

getPlans

@RequestBody

planGetRequest

request

);

}

然後我們在

Controller

就可以像原來呼叫

Service

層程式碼一樣呼叫它了。

@RestController

public

class

TestController

{

// 這裡就相當於原來自動注入的 Service

@Autowired

private

TestClient

testClient

// controller 呼叫 service 層程式碼

@RequestMapping

value

=

“/test”

method

=

RequestMethod

POST

public

CommonResponse

<

List

<

Plan

>>

get

@RequestBody

planGetRequest

request

{

return

testClient

getPlans

request

);

}

}

必不可少的 Hystrix

什麼是 Hystrix之熔斷和降級

Spring Cloud 入門總結

在分散式環境中,不可避免地會有許多服務依賴項中的某些失敗。Hystrix是一個庫,可透過新增等待時間容限和容錯邏輯來幫助您控制這些分散式服務之間的互動。Hystrix透過隔離服務之間的訪問點,停止服務之間的級聯故障並提供後備選項來實現此目的,所有這些都可以提高系統的整體彈性。

總體來說

Hystrix

就是一個能進行

熔斷

降級

的庫,透過使用它能提高整個系統的彈性。

那麼什麼是 熔斷和降級 呢?再舉個 ,此時我們整個微服務系統是這樣的。服務A呼叫了服務B,服務B再呼叫了服務C,但是因為某些原因,服務C頂不住了,這個時候大量請求會在服務C阻塞。

Spring Cloud 入門總結

服務C阻塞了還好,畢竟只是一個系統崩潰了。但是請注意這個時候因為服務C不能返回響應,那麼服務B呼叫服務C的的請求就會阻塞,同理服務B阻塞了,那麼服務A也會阻塞崩潰。

請注意,為什麼阻塞會崩潰。因為這些請求會消耗佔用系統的執行緒、IO 等資源,消耗完你這個系統伺服器不就崩了麼。

Spring Cloud 入門總結

這就叫

服務雪崩

。媽耶,上面兩個

熔斷

降級

你都沒給我解釋清楚,你現在又給我扯什麼

服務雪崩

別急,聽我慢慢道來。

Spring Cloud 入門總結

不聽我也得講下去!

所謂

熔斷

就是服務雪崩的一種有效解決方案。當指定時間窗內的請求失敗率達到設定閾值時,系統將透過

斷路器

直接將此請求鏈路斷開。

也就是我們上面服務B呼叫服務C在指定時間窗內,呼叫的失敗率到達了一定的值,那麼

Hystrix

則會自動將 服務B與C 之間的請求都斷了,以免導致服務雪崩現象。

其實這裡所講的

熔斷

就是指的

Hystrix

中的

斷路器模式

,你可以使用簡單的

@HystrixCommand

註解來標註某個方法,這樣

Hystrix

就會使用

斷路器

來“包裝”這個方法,每當呼叫時間超過指定時間時(預設為1000ms),斷路器將會中斷對這個方法的呼叫。

當然你可以對這個註解的很多屬性進行設定,比如設定超時時間,像這樣。

@HystrixCommand

commandProperties

=

{

@HystrixProperty

name

=

“execution。isolation。thread。timeoutInMilliseconds”

value

=

“1200”

)}

public

List

<

Xxx

>

getXxxx

()

{

// 。。。省略程式碼邏輯

}

但是,我查閱了一些部落格,發現他們都將

熔斷

降級

的概念混淆了,以我的理解,

降級是為了更好的使用者體驗,當一個方法呼叫異常時,透過執行另一種程式碼邏輯來給使用者友好的回覆

。這也就對應著

Hystrix

後備處理

模式。你可以透過設定

fallbackMethod

來給一個方法設定備用的程式碼邏輯。比如這個時候有一個熱點新聞出現了,我們會推薦給使用者檢視詳情,然後使用者會透過id去查詢新聞的詳情,但是因為這條新聞太火了(比如最近什麼*易對吧),大量使用者同時訪問可能會導致系統崩潰,那麼我們就進行

服務降級

,一些請求會做一些降級處理比如當前人數太多請稍後檢視等等。

// 指定了後備方法呼叫

@HystrixCommand

fallbackMethod

=

“getHystrixNews”

@GetMapping

“/get/news”

public

News

getNews

@PathVariable

“id”

int

id

{

// 呼叫新聞系統的獲取新聞api 程式碼邏輯省略

}

//

public

News

getHystrixNews

@PathVariable

“id”

int

id

{

// 做服務降級

// 返回當前人數太多,請稍後檢視

}

什麼是Hystrix之其他

我在閱讀 《Spring微服務實戰》這本書的時候還接觸到了一個

艙壁模式

的概念。在不使用艙壁模式的情況下,服務A呼叫服務B,這種呼叫預設的是

使用同一批執行緒來執行

的,而在一個服務出現效能問題的時候,就會出現所有執行緒被刷爆並等待處理工作,同時阻塞新請求,最終導致程式崩潰。而艙壁模式會將遠端資源呼叫隔離在他們自己的執行緒池中,以便可以控制單個表現不佳的服務,而不會使該程式崩潰。

具體其原理我推薦大家自己去了解一下,本篇文章中對

艙壁模式

不做過多解釋。當然還有

Hystrix

儀表盤

,它是

用來實時監控

Hystrix

的各項指標資訊的

,這裡我將這個問題也丟擲去,希望有不瞭解的可以自己去搜索一下。

微服務閘道器——Zuul

Spring Cloud 入門總結

ZUUL 是從裝置和 web 站點到 Netflix 流應用後端的所有請求的前門。作為邊界服務應用,ZUUL 是為了實現動態路由、監視、彈性和安全性而構建的。它還具有根據情況將請求路由到多個 Amazon Auto Scaling Groups(亞馬遜自動縮放組,亞馬遜的一種雲計算方式) 的能力

在上面我們學習了

Eureka

之後我們知道了

服務提供者

消費者

透過

Eureka Server

進行訪問的,即

Eureka Server

服務提供者

的統一入口。那麼整個應用中存在那麼多

消費者

需要使用者進行呼叫,這個時候使用者該怎樣訪問這些

消費者工程

呢?當然可以像之前那樣直接訪問這些工程。但這種方式沒有統一的消費者工程呼叫入口,不便於訪問與管理,而 Zuul 就是這樣的一個對於

消費者

的統一入口。

如果學過前端的肯定都知道 Router 吧,比如 Flutter 中的路由,Vue,React中的路由,用了 Zuul 你會發現在路由功能方面和前端配置路由基本是一個理。 我偶爾擼擼 Flutter。

大家對閘道器應該很熟吧,簡單來講閘道器是系統唯一對外的入口,介於客戶端與伺服器端之間,用於對請求進行

鑑權

限流

路由

監控

等功能。

Spring Cloud 入門總結

沒錯,閘道器有的功能,

Zuul

基本都有。而

Zuul

中最關鍵的就是

路由和過濾器

了,在官方文件中

Zuul

的標題就是

Router and Filter : Zuul

Zuul 的路由功能

簡單配置

本來想給你們複製一些程式碼,但是想了想,因為各個程式碼配置比較零散,看起來也比較零散,我決定還是給你們畫個圖來解釋吧。

請不要因為我這麼好就給我點贊 。 瘋狂暗示。

比如這個時候我們已經向

Eureka Server

註冊了兩個

Consumer

、三個

Provicer

,這個時候我們再加個

Zuul

閘道器應該變成這樣子了。

Spring Cloud 入門總結

emmm,資訊量有點大,我來解釋一下。關於前面的知識我就不解釋了 。

首先,

Zuul

需要向

Eureka

進行註冊,註冊有啥好處呢?

你傻呀,

Consumer

都向

Eureka Server

進行註冊了,我閘道器是不是隻要註冊就能拿到所有

Consumer

的資訊了?

拿到資訊有什麼好處呢?

我拿到資訊我是不是可以獲取所有的

Consumer

的元資料(名稱,ip,埠)?

拿到這些元資料有什麼好處呢?拿到了我們是不是直接可以做

路由對映

?比如原來使用者呼叫

Consumer1

的介面

localhost:8001/studentInfo/update

這個請求,我們是不是可以這樣進行呼叫了呢?

localhost:9000/consumer1/studentInfo/update

呢?你這樣是不是恍然大悟了?

這裡的url為了讓更多人看懂所以沒有使用 restful 風格。

上面的你理解了,那麼就能理解關於

Zuul

最基本的配置了,看下面。

server:

port

9000

eureka:

client

service

-

url

#

這裡只要註冊

Eureka

就行了

defaultZone

http

://

localhost

9997

/

eureka

然後在啟動類上加入

@EnableZuulProxy

註解就行了。沒錯,就是那麼簡單 。

統一字首

這個很簡單,就是我們可以在前面加一個統一的字首,比如我們剛剛呼叫的是

localhost:9000/consumer1/studentInfo/update

,這個時候我們在

yaml

配置檔案中新增如下。

zuul:

prefix

/

zuul

這樣我們就需要透過

localhost:9000/zuul/consumer1/studentInfo/update

來進行訪問了。

路由策略配置

你會發現前面的訪問方式(直接使用服務名),需要將微服務名稱暴露給使用者,會存在安全性問題。所以,可以自定義路徑來替代微服務名稱,即自定義路由策略。

zuul:

routes

consumer1

/

FrancisQ1

/**

consumer2

/

FrancisQ2

/**

這個時候你就可以使用

localhost:9000/zuul/FrancisQ1/studentInfo/update

進行訪問了。

服務名遮蔽

這個時候你別以為你好了,你可以試試,在你配置完路由策略之後使用微服務名稱還是可以訪問的,這個時候你需要將服務名遮蔽。

zuul:

ignore

-

services

“*”

路徑遮蔽

Zuul

還可以指定遮蔽掉的路徑 URI,即只要使用者請求中包含指定的 URI 路徑,那麼該請求將無法訪問到指定的服務。透過該方式可以限制使用者的許可權。

zuul:

ignore

-

patterns

**/

auto

/**

這樣關於 auto 的請求我們就可以過濾掉了。

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

*代表匹配一級任意路徑

敏感請求頭遮蔽

預設情況下,像 Cookie、Set-Cookie 等敏感請求頭資訊會被 zuul 遮蔽掉,我們可以將這些預設遮蔽去掉,當然,也可以新增要遮蔽的請求頭。

Zuul 的過濾功能

如果說,路由功能是

Zuul

的基操的話,那麼

過濾器

就是

Zuul

的利器了。畢竟所有請求都經過閘道器(Zuul),那麼我們可以進行各種過濾,這樣我們就能實現

限流

灰度釋出

許可權控制

等等。

簡單實現一個請求時間日誌列印

要實現自己定義的

Filter

我們只需要繼承

ZuulFilter

然後將這個過濾器類以

@Component

註解加入 Spring 容器中就行了。

在給你們看程式碼之前我先給你們解釋一下關於過濾器的一些注意點。

Spring Cloud 入門總結

過濾器型別:Pre、Routing、Post。前置Pre就是在請求之前進行過濾,Routing路由過濾器就是我們上面所講的路由策略,而Post後置過濾器就是在

Response

之前進行過濾的過濾器。你可以觀察上圖結合著理解,並且下面我會給出相應的註釋。

// 加入Spring容器

@Component

public

class

PreRequestFilter

extends

ZuulFilter

{

// 返回過濾器型別 這裡是前置過濾器

@Override

public

String

filterType

()

{

return

FilterConstants

PRE_TYPE

}

// 指定過濾順序 越小越先執行,這裡第一個執行

// 當然不是隻真正第一個 在Zuul內建中有其他過濾器會先執行

// 那是寫死的 比如 SERVLET_DETECTION_FILTER_ORDER = -3

@Override

public

int

filterOrder

()

{

return

0

}

// 什麼時候該進行過濾

// 這裡我們可以進行一些判斷,這樣我們就可以過濾掉一些不符合規定的請求等等

@Override

public

boolean

shouldFilter

()

{

return

true

}

// 如果過濾器允許透過則怎麼進行處理

@Override

public

Object

run

()

throws

ZuulException

{

// 這裡我設定了全域性的RequestContext並記錄了請求開始時間

RequestContext

ctx

=

RequestContext

getCurrentContext

();

ctx

set

“startTime”

System

currentTimeMillis

());

return

null

}

}

// lombok的日誌

@Slf4j

// 加入 Spring 容器

@Component

public

class

AccessLogFilter

extends

ZuulFilter

{

// 指定該過濾器的過濾型別

// 此時是後置過濾器

@Override

public

String

filterType

()

{

return

FilterConstants

POST_TYPE

}

// SEND_RESPONSE_FILTER_ORDER 是最後一個過濾器

// 我們此過濾器在它之前執行

@Override

public

int

filterOrder

()

{

return

FilterConstants

SEND_RESPONSE_FILTER_ORDER

-

1

}

@Override

public

boolean

shouldFilter

()

{

return

true

}

// 過濾時執行的策略

@Override

public

Object

run

()

throws

ZuulException

{

RequestContext

context

=

RequestContext

getCurrentContext

();

HttpServletRequest

request

=

context

getRequest

();

// 從RequestContext獲取原先的開始時間 並透過它計算整個時間間隔

Long

startTime

=

Long

context

get

“startTime”

);

// 這裡我可以獲取HttpServletRequest來獲取URI並且打印出來

String

uri

=

request

getRequestURI

();

long

duration

=

System

currentTimeMillis

()

-

startTime

log

info

“uri: ”

+

uri

+

“, duration: ”

+

duration

/

100

+

“ms”

);

return

null

}

}

上面就簡單實現了請求時間日誌列印功能,你有沒有感受到

Zuul

過濾功能的強大了呢?

沒有?好的、那我們再來。

令牌桶限流

當然不僅僅是令牌桶限流方式,

Zuul

只要是限流的活它都能幹,這裡我只是簡單舉個 。

Spring Cloud 入門總結

我先來解釋一下什麼是

令牌桶限流

吧。

首先我們會有個桶,如果裡面沒有滿那麼就會以一定

固定的速率

會往裡面放令牌,一個請求過來首先要從桶中獲取令牌,如果沒有獲取到,那麼這個請求就拒絕,如果獲取到那麼就放行。很簡單吧,啊哈哈、

下面我們就透過

Zuul

的前置過濾器來實現一下令牌桶限流。

@Component

@Slf4j

public

class

RouteFilter

extends

ZuulFilter

{

// 定義一個令牌桶,每秒產生2個令牌,即每秒最多處理2個請求

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

“訪問量超載”

);

// 指定當前請求未透過過濾

context

setSendZuulResponse

false

);

// 向客戶端返回響應碼429,請求數量過多

context

setResponseStatusCode

429

);

return

false

}

return

true

}

}

這樣我們就能將請求數量控制在一秒兩個,有沒有覺得很酷?

關於 Zuul 的其他

Zuul

的過濾器的功能肯定不止上面我所實現的兩種,它還可以實現

許可權校驗

,包括我上面提到的

灰度釋出

等等。

當然,

Zuul

作為閘道器肯定也存在

單點問題

,如果我們要保證

Zuul

的高可用,我們就需要進行

Zuul

的叢集配置,這個時候可以藉助額外的一些負載均衡器比如

Nginx

Spring Cloud配置管理——Config

為什麼要使用進行配置管理?

當我們的微服務系統開始慢慢地龐大起來,那麼多

Consumer

Provider

Eureka Server

Zuul

系統都會持有自己的配置,這個時候我們在專案執行的時候可能需要更改某些應用的配置,如果我們不進行配置的統一管理,我們只能

去每個應用下一個一個尋找配置檔案然後修改配置檔案再重啟應用

首先對於分散式系統而言我們就不應該去每個應用下去分別修改配置檔案,再者對於重啟應用來說,服務無法訪問所以直接拋棄了可用性,這是我們更不願見到的。

那麼有沒有一種方法

既能對配置檔案統一地進行管理,又能在專案執行時動態修改配置檔案呢?

那就是我今天所要介紹的

Spring Cloud Config

能進行配置管理的框架不止

Spring Cloud Config

一種,大家可以根據需求自己選擇(disconf,阿波羅等等)。而且對於

Config

來說有些地方實現的不是那麼盡人意。

Config 是什麼

Spring Cloud Config

為分散式系統中的外部化配置提供伺服器和客戶端支援。使用

Config

伺服器,可以在中心位置管理所有環境中應用程式的外部屬性。

簡單來說,

Spring Cloud Config

就是能將各個 應用/系統/模組 的配置檔案存放到

統一的地方然後進行管理

(Git 或者 SVN)。

你想一下,我們的應用是不是隻有啟動的時候才會進行配置檔案的載入,那麼我們的

Spring Cloud Config

就暴露出一個介面給啟動應用來獲取它所想要的配置檔案,應用獲取到配置檔案然後再進行它的初始化工作。就如下圖。

Spring Cloud 入門總結

當然這裡你肯定還會有一個疑問,如果我在應用執行時去更改遠端配置倉庫(Git)中的對應配置檔案,那麼依賴於這個配置檔案的已啟動的應用會不會進行其相應配置的更改呢?

答案是不會的。

什麼?那怎麼進行動態修改配置檔案呢?這不是出現了

配置漂移

嗎?你個渣男 ,你又騙我!

別急嘛,你可以使用

Webhooks

,這是

github

提供的功能,它能確保遠端庫的配置檔案更新後客戶端中的配置資訊也得到更新。

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

慢著,聽我說完,

Webhooks

雖然能解決,但是你瞭解一下會發現它根本不適合用於生產環境,所以基本不會使用它的。

Spring Cloud 入門總結

而一般我們會使用

Bus

訊息匯流排 +

Spring Cloud Config

進行配置的動態重新整理。

引出 Spring Cloud Bus

用於將服務和服務例項與分散式訊息系統連結在一起的事件匯流排。在叢集中傳播狀態更改很有用(例如配置更改事件)。

你可以簡單理解為

Spring Cloud Bus

的作用就是

管理和廣播分散式系統中的訊息

,也就是訊息引擎系統中的廣播模式。當然作為

訊息匯流排

Spring Cloud Bus

可以做很多事而不僅僅是客戶端的配置重新整理功能。

而擁有了

Spring Cloud Bus

之後,我們只需要建立一個簡單的請求,並且加上

@ResfreshScope

註解就能進行配置的動態修改了,下面我畫了張圖供你理解。

Spring Cloud 入門總結

總結

這篇文章中我帶大家初步瞭解了

Spring Cloud

的各個元件,他們有

Eureka 服務發現框架

Ribbon 程序內負載均衡器

Open Feign 服務呼叫對映

Hystrix 服務降級熔斷器

Zuul 微服務閘道器

Config 微服務統一配置中心

Bus 訊息匯流排

如果你能這個時候能看懂下面那張圖,也就說明了你已經對

Spring Cloud

微服務有了一定的架構認識。

Spring Cloud 入門總結

如果覺得我寫的還不錯,那就留下個贊吧!

作者:FrancisQ

連結:

https://

juejin。im/post/5de2553e

5188256e885f4fa3

標簽: eureka  服務  Spring  呼叫  請求