概述 html
首先我給你們看一張圖,若是你們對這張圖有些地方不太理解的話,我但願大家看完我這篇文章會恍然大悟。前端
構建分佈式系統不須要複雜和容易出錯。Spring Cloud 爲最多見的分佈式系統模式提供了一種簡單且易於接受的編程模型,幫助開發人員構建有彈性的、可靠的、協調的應用程序。Spring Cloud 構建於 Spring Boot 之上,使得開發者很容易入手並快速應用於生產中。git
官方果真官方,介紹都這麼有板有眼的。github
我所理解的Spring Cloud就是微服務系統架構的一站式解決方案,在平時咱們構建微服務的過程當中須要作如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等操做,而 Spring Cloud 爲咱們提供了一套簡易的編程模型,使咱們能在 Spring Boot 的基礎上輕鬆地實現微服務項目的構建。web
Spring Cloud 的版本固然這個只是個題外話。算法
Spring Cloud 的版本號並非咱們一般見的數字版本號,而是一些很奇怪的單詞。這些單詞均爲英國倫敦地鐵站的站名。同時根據字母表的順序來對應版本時間順序,好比:最先 的 Release 版本 Angel,第二個 Release 版本 Brixton(英國地名),而後是 Camden、 Dalston、Edgware、Finchley、Greenwich、Hoxton。編程
Spring Cloud 的服務發現框架——EurekaEureka是基於REST(表明性狀態轉移)的服務,主要在AWS雲中用於定位服務,以實現負載均衡和中間層服務器的故障轉移。咱們稱此服務爲Eureka服務器。Eureka還帶有一個基於Java的客戶端組件Eureka Client,它使與服務的交互變得更加容易。客戶端還具備一個內置的負載平衡器,能夠執行基本的循環負載平衡。在Netflix,更復雜的負載均衡器將Eureka包裝起來,以基於流量,資源使用,錯誤條件等多種因素提供加權負載均衡,以提供出色的彈性。後端
總的來講,Eureka 就是一個服務發現框架。何爲服務,何又爲發現呢?api
舉一個生活中的例子,就好比咱們平時租房子找中介的事情。緩存
在沒有中介的時候咱們須要一個一個去尋找是否有房屋要出租的房東,這顯然會很是的費力,一你找憑一我的的能力是找不到不少房源供你選擇,再者你也懶得這麼找下去(找了這麼久,沒有合適的只能將就)。這裏的咱們就至關於微服務中的Consumer,而那些房東就至關於微服務中的Provider。消費者Consumer須要調用提供者Provider提供的一些服務,就像咱們如今須要租他們的房子同樣。
可是若是隻是租客和房東之間進行尋找的話,他們的效率是很低的,房東找不到租客賺不到錢,租客找不到房東住不了房。因此,後來房東確定就想到了廣播本身的房源信息(好比在街邊貼貼小廣告),這樣對於房東來講已經完成他的任務(將房源公佈出去),可是有兩個問題就出現了。
第1、其餘不是租客的都能收到這種租房消息,這在現實世界沒什麼,可是在計算機的世界中就會出現資源消耗的問題了。第2、租客這樣仍是很難找到你,試想一下我須要租房,我還須要東一個西一個地去找街邊小廣告,麻不麻煩?
那怎麼辦呢?咱們固然不會那麼傻乎乎的,第一時間就是去找中介呀,它爲咱們提供了統一房源的地方,咱們消費者只須要跑到它那裏去找就好了。而對於房東來講,他們也只須要把房源在中介那裏發佈就好了。
那麼如今,咱們的模式就是這樣的了。
可是,這個時候還會出現一些問題。
房東註冊以後若是不想賣房子了怎麼辦?咱們是否是須要讓房東按期續約?若是房東不進行續約是否是要將他們從中介那裏的註冊列表中移除。
租客是否是也要進行註冊呢?否則合同乙方怎麼來呢?
中介可不能夠作連鎖店呢?若是這一個店由於某些不可抗力因素而沒法使用,那麼咱們是否能夠換一個連鎖店呢?
針對上面的問題咱們來從新構建一下上面的模式圖。
好了,舉完這個例子咱們就能夠來看關於 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] 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 架構圖,你會發現和咱們前面畫的中介圖別無二致。
固然,能夠充當服務發現的組件有不少:Zookeeper,Consul, Eureka 等。
更多關於 Eureka 的知識(自我保護,初始註冊策略等等)能夠本身去官網查看,或者查看個人另外一篇文章 [深刻理解 Eureka]
負載均衡之 Ribbon不是講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 是Netflix公司的一個開源的負載均衡 項目,是一個客戶端/進程內負載均衡器,運行在消費者端。
咱們再舉個例子,好比咱們設計了一個秒殺系統,可是爲了整個系統的高可用,咱們須要將這個系統作一個集羣,而這個時候咱們消費者就能夠擁有多個秒殺系統的調用途徑了,以下圖。
若是這個時候咱們沒有進行一些均衡操做,若是咱們對秒殺系統1進行大量的調用,而另外兩個基本不請求,就會致使秒殺系統1崩潰,而另外兩個就變成了傀儡,那麼咱們爲何還要作集羣,咱們高可用體現的意義又在哪呢?
因此Ribbon出現了,注意咱們上面加粗的幾個字——運行在消費者端。指的是,Ribbon是運行在消費者端的負載均衡器,以下圖。
其工做原理就是Consumer端獲取到了全部的服務列表以後,在其內部使用負載均衡算法,進行對多個系統的調用。
提到負載均衡就不得不提到大名鼎鼎的Nignx了,而和Ribbon不一樣的是,它是一種集中式的負載均衡器。
何爲集中式呢?簡單理解就是將全部請求都集中起來,而後再進行負載均衡。以下圖。
咱們能夠看到Nginx是接收了全部的請求進行負載均衡的,而對於Ribbon來講它是在消費者端進行的負載均衡。以下圖。
請注意Request的位置,在Nginx中請求是先進入負載均衡器,而在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是一個庫,可經過添加等待時間容限和容錯邏輯來幫助您控制這些分佈式服務之間的交互。Hystrix經過隔離服務之間的訪問點,中止服務之間的級聯故障並提供後備選項來實現此目的,全部這些均可以提升系統的總體彈性。
整體來講[Hystrix]就是一個能進行熔斷和降級的庫,經過使用它能提升整個系統的彈性。
那麼什麼是 熔斷和降級 呢?再舉個例子,此時咱們整個微服務系統是這樣的。服務A調用了服務B,服務B再調用了服務C,可是由於某些緣由,服務C頂不住了,這個時候大量請求會在服務C阻塞。
服務C阻塞了還好,畢竟只是一個系統崩潰了。可是請注意這個時候由於服務C不能返回響應,那麼服務B調用服務C的的請求就會阻塞,同理服務B阻塞了,那麼服務A也會阻塞崩潰。
請注意,爲何阻塞會崩潰。由於這些請求會消耗佔用系統的線程、IO 等資源,消耗完你這個系統服務器不就崩了麼。
這就叫服務雪崩。媽耶,上面兩個熔斷和降級你都沒給我解釋清楚,你如今又給我扯什麼服務雪崩?
別急,聽我慢慢道來。更多的關於設計,原理知識點的問題,能夠在互聯網架構師後臺回覆2T獲取。
不聽我也得講下去!
所謂熔斷就是服務雪崩的一種有效解決方案。當指定時間窗內的請求失敗率達到設定閾值時,系統將經過斷路器直接將此請求鏈路斷開。
也就是咱們上面服務B調用服務C在指定時間窗內,調用的失敗率到達了必定的值,那麼[Hystrix]則會自動將 服務B與C 之間的請求都斷了,以避免致使服務雪崩現象。
其實這裏所講的熔斷就是指的[Hystrix]中的斷路器模式,你可使用簡單的@[Hystrix]Command註解來標註某個方法,這樣[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) { // 作服務降級 // 返回當前人數太多,請稍後查看 }
我在閱讀 《Spring微服務實戰》這本書的時候還接觸到了一個艙壁模式的概念。在不使用艙壁模式的狀況下,服務A調用服務B,這種調用默認的是使用同一批線程來執行的,而在一個服務出現性能問題的時候,就會出現全部線程被刷爆並等待處理工做,同時阻塞新請求,最終致使程序崩潰。而艙壁模式會將遠程資源調用隔離在他們本身的線程池中,以即可以控制單個表現不佳的服務,而不會使該程序崩潰。
具體其原理我推薦你們本身去了解一下,本篇文章中對艙壁模式不作過多解釋。固然還有[Hystrix]儀表盤,它是用來實時監控****[Hystrix]的各項指標信息的,這裏我將這個問題也拋出去,但願有不瞭解的能夠本身去搜索一下。
微服務網關——ZuulZUUL 是從設備和 web 站點到 Netflix 流應用後端的全部請求的前門。做爲邊界服務應用,ZUUL 是爲了實現動態路由、監視、彈性和安全性而構建的。它還具備根據狀況將請求路由到多個 Amazon Auto Scaling Groups(亞馬遜自動縮放組,亞馬遜的一種雲計算方式) 的能力
在上面咱們學習了 Eureka 以後咱們知道了服務提供者是消費者經過[Eureka] Server進行訪問的,即[Eureka] Server是服務提供者的統一入口。那麼整個應用中存在那麼多消費者須要用戶進行調用,這個時候用戶該怎樣訪問這些消費者工程呢?固然能夠像以前那樣直接訪問這些工程。但這種方式沒有統一的消費者工程調用入口,不便於訪問與管理,而 Zuul 就是這樣的一個對於消費者的統一入口。
若是學過前端的確定都知道 Router 吧,好比 Flutter 中的路由,Vue,React中的路由,用了 Zuul 你會發如今路由功能方面和前端配置路由基本是一個理,我偶爾擼擼 Flutter。關注微信公衆號:Java技術棧,在後臺回覆:cloud,能夠獲取 Spring Cloud 系列教程。
你們對網關應該很熟吧,簡單來說網關是系統惟一對外的入口,介於客戶端與服務器端之間,用於對請求進行鑑權、限流、路由、監控等功能。
沒錯,網關有的功能,Zuul基本都有。而Zuul中最關鍵的就是路由和過濾器了,在官方文檔中Zuul的標題就是
Router and Filter : Zuul
原本想給大家複製一些代碼,可是想了想,由於各個代碼配置比較零散,看起來也比較零散,我決定仍是給大家畫個圖來解釋吧。
好比這個時候咱們已經向[Eureka] Server註冊了兩個Consumer、三個Provicer,這個時候咱們再加個Zuul網關應該變成這樣子了。
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),那麼咱們能夠進行各類過濾,這樣咱們就能實現限流,灰度發佈,權限控制等等。
要實現本身定義的Filter咱們只須要繼承ZuulFilter而後將這個過濾器類以@Component註解加入 Spring 容器中就好了。
在給大家看代碼以前我先給大家解釋一下關於過濾器的一些注意點。
過濾器類型: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過濾功能的強大了呢?Spring Cloud Gateway VS Zuul 比較,怎麼選?
固然不只僅是令牌桶限流方式,Zuul只要是限流的活它都能幹,這裏我只是簡單舉個例子。
我先來解釋一下什麼是令牌桶限流吧。
首先咱們會有個桶,若是裏面沒有滿那麼就會以必定固定的速率會往裏面放令牌,一個請求過來首先要從桶中獲取令牌,若是沒有獲取到,那麼這個請求就拒絕,若是獲取到那麼就放行。很簡單吧,啊哈哈、
下面咱們就經過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的集羣配置,這個時候能夠藉助額外的一些負載均衡器好比Nginx。
Spring Cloud配置管理——Config當咱們的微服務系統開始慢慢地龐大起來,那麼多Consumer、Provider、[Eureka] Server、Zuul系統都會持有本身的配置,這個時候咱們在項目運行的時候可能須要更改某些應用的配置,若是咱們不進行配置的統一管理,咱們只能去每一個應用下一個一個尋找配置文件而後修改配置文件再重啓應用。
首先對於分佈式系統而言咱們就不該該去每一個應用下去分別修改配置文件,再者對於重啓應用來講,服務沒法訪問因此直接拋棄了可用性,這是咱們更不肯見到的。
那麼有沒有一種方法既能對配置文件統一地進行管理,又能在項目運行時動態修改配置文件呢?
那就是我今天所要介紹的Spring Cloud Config。
能進行配置管理的框架不止Spring Cloud Config一種,你們能夠根據需求本身選擇(disconf,阿波羅等等)。並且對於Config來講有些地方實現的不是那麼盡人意。
Spring Cloud Config爲分佈式系統中的外部化配置提供服務器和客戶端支持。使用Config服務器,能夠在中心位置管理全部環境中應用程序的外部屬性。關注微信公衆號:Java技術棧,在後臺回覆:cloud,能夠獲取 Spring Cloud 系列教程。
簡單來講,Spring Cloud Config就是能將各個 應用/系統/模塊 的配置文件存放到統一的地方而後進行管理(Git 或者 SVN)。
你想一下,咱們的應用是否是隻有啓動的時候纔會進行配置文件的加載,那麼咱們的Spring Cloud Config就暴露出一個接口給啓動應用來獲取它所想要的配置文件,應用獲取到配置文件而後再進行它的初始化工做。就以下圖。
固然這裏你確定還會有一個疑問,若是我在應用運行時去更改遠程配置倉庫(Git)中的對應配置文件,那麼依賴於這個配置文件的已啓動的應用會不會進行其相應配置的更改呢?
答案是不會的。
什麼?那怎麼進行動態修改配置文件呢?這不是出現了配置漂移嗎?你個渣男,你又騙我!
別急嘛,你可使用Webhooks,這是 github提供的功能,它能確保遠程庫的配置文件更新後客戶端中的配置信息也獲得更新。
噢噢,這還差很少。我去查查怎麼用。
慢着,聽我說完,Webhooks雖然能解決,可是你瞭解一下會發現它根本不適合用於生產環境,因此基本不會使用它的。
而通常咱們會使用Bus消息總線 +Spring Cloud Config進行配置的動態刷新。
引出 Spring Cloud Bus用於將服務和服務實例與分佈式消息系統連接在一塊兒的事件總線。在集羣中傳播狀態更改頗有用(例如配置更改事件)。關注微信公衆號:Java技術棧,在後臺回覆:cloud,能夠獲取 Spring Cloud 系列教程。
你能夠簡單理解爲Spring Cloud Bus的做用就是管理和廣播分佈式系統中的消息,也就是消息引擎系統中的廣播模式。固然做爲消息總線的Spring Cloud Bus能夠作不少事而不只僅是客戶端的配置刷新功能。
而擁有了Spring Cloud Bus以後,咱們只須要建立一個簡單的請求,而且加上@ResfreshScope註解就能進行配置的動態修改了,下面我畫了張圖供你理解。
這篇文章中我帶你們初步瞭解了Spring Cloud的各個組件,他們有
Eureka 服務發現框架
Ribbon 進程內負載均衡器
Open Feign 服務調用映射
Hystrix 服務降級熔斷器
Zuul 微服務網關
Config 微服務統一配置中心
Bus 消息總線
若是你能這個時候能看懂下面那張圖,也就說明了你已經對Spring Cloud微服務有了必定的架構認識。
做者:FrancisQ
juejin.im/post/5de2553e5188256e885f4fa3