<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Cloud 總結(jié)!

          共 15852字,需瀏覽 32分鐘

           ·

          2020-08-16 12:32

          來源:FrancisQ

          鏈接:juejin.im/post/6844904007975043079


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

          什么是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)注冊?、配置中心?、消息總線?、負載均衡?、斷路器?、數(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)負載均衡和中間層服務(wù)器的故障轉(zhuǎn)移。我們稱此服務(wù)為Eureka服務(wù)器。Eureka還帶有一個基于Java的客戶端組件Eureka Client,它使與服務(wù)的交互變得更加容易。客戶端還具有一個內(nèi)置的負載平衡器,可以執(zhí)行基本的循環(huán)負載平衡。在Netflix,更復(fù)雜的負載均衡器將Eureka包裝起來,以基于流量,資源使用,錯誤條件等多種因素提供加權(quán)負載均衡,以提供出色的彈性。

          總的來說,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īng)完成他的任務(wù)(將房源公布出去),但是有兩個問題就出現(xiàn)了。第一、其他不是租客的都能收到這種租房消息,這在現(xiàn)實世界沒什么,但是在計算機的世界中就會出現(xiàn)資源消耗?的問題了。第二、租客這樣還是很難找到你,試想一下我需要租房,我還需要東一個西一個地去找街邊小廣告,麻不麻煩?

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

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

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

          1. 房東注冊之后如果不想賣房子了怎么辦?我們是不是需要讓房東定期續(xù)約??如果房東不進行續(xù)約是不是要將他們從中介那里的注冊列表中移除?。
          2. 租客是不是也要進行注冊?呢?不然合同乙方怎么來呢?
          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秒(默認情況下)發(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ù),從而進行遠程調(diào)用。該注冊列表信息定期(每30秒鐘)更新一次。每次返回注冊列表信息可能與?Eureka?客戶端的緩存信息不同,?Eureka?客戶端自動處理。如果由于某種原因?qū)е伦粤斜硇畔⒉荒芗皶r匹配,Eureka?客戶端則會重新獲取整個注冊表信息。Eureka?服務(wù)器緩存注冊列表信息,整個注冊表以及每個應(yīng)用程序的信息進行了壓縮,壓縮內(nèi)容和沒有壓縮的內(nèi)容完全相同。Eureka?客戶端和Eureka?服務(wù)器可以使用JSON / XML格式進行通訊。在默認的情況下?Eureka?客戶端使用壓縮?JSON?格式來獲取注冊列表的信息。

          結(jié)合中介理解:租客(消費者?Eureka Client Consumer) 去中介 (服務(wù)器?Eureka Server) 那里獲取所有的房屋信息列表 (客戶端列表?Eureka Client List) ,而且租客為了獲取最新的信息會定期向中介 (服務(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

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

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

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

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

          更多關(guān)于 Eureka 的知識(自我保護,初始注冊策略等等)可以自己去官網(wǎng)查看,或者查看我的另一篇文章 [深入理解 Eureka](https://juejin.im/post/5dd497e3f265da0ba7718018)。

          負載均衡之 Ribbon

          什么是 RestTemplate?

          不是講?Ribbon?么?怎么扯到了?RestTemplate?了?你先別急,聽我慢慢道來。

          我不聽我不聽我不聽???。

          我就說一句!RestTemplateSpring提供的一個訪問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?公司的一個開源的負載均衡 項目,是一個客戶端/進程內(nèi)負載均衡器,運行在消費者端?。

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

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

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

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

          Nginx 和 Ribbon 的對比

          提到?負載均衡?就不得不提到大名鼎鼎的?Nignx?了,而和?Ribbon?不同的是,它是一種集中式?的負載均衡器。

          何為集中式呢?簡單理解就是?將所有請求都集中起來,然后再進行負載均衡?。如下圖。

          我們可以看到?Nginx?是接收了所有的請求進行負載均衡的,而對于?Ribbon?來說它是在消費者端進行的負載均衡。如下圖。

          請注意?Request?的位置,在?Nginx?中請求是先進入負載均衡器,而在?Ribbon?中是先在客戶端進行負載均衡才進行請求的。

          Ribbon 的幾種負載均衡算法

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

          • RoundRobinRule?:輪詢策略。Ribbon?默認采用的策略。若經(jīng)過一輪輪詢沒有找到可用的?provider,其最多輪詢 10 輪。若最終還沒有找到,則返回 null。
          • RandomRule?: 隨機策略,從所有可用的 provider 中隨機選擇一個。
          • RetryRule?: 重試策略。先按照 RoundRobinRule 策略獲取 provider,若獲取失敗,則在指定的時限內(nèi)重試。默認的時限為 500 毫秒。

          ??? 還有很多,這里不一一舉?了,你最需要知道的是默認輪詢算法,并且可以更換默認的負載均衡算法,只需要在配置文件中做出修改就行。

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

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

          什么是 Open Feign

          有了 Eureka,RestTemplateRibbon?我們就可以?愉快地進行服務(wù)間的調(diào)用了,但是使用?RestTemplate?還是不方便,我們每次都要進行這樣的調(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)用原來代碼一樣進行各個服務(wù)間的調(diào)用呢?

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

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

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

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

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

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

          必不可少的 Hystrix

          什么是 Hystrix之熔斷和降級

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

          總體來說 Hystrix 就是一個能進行?熔斷?和?降級?的庫,通過使用它能提高整個系統(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)的請求失敗率達到設(shè)定閾值時,系統(tǒng)將通過 斷路器 直接將此請求鏈路斷開。
          也就是我們上面服務(wù)B調(diào)用服務(wù)C在指定時間窗內(nèi),調(diào)用的失敗率到達了一定的值,那么 Hystrix 則會自動將 服務(wù)B與C 之間的請求都斷了,以免導(dǎo)致服務(wù)雪崩現(xiàn)象。


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

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

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

          但是,我查閱了一些博客,發(fā)現(xiàn)他們都將?熔斷?和?降級?的概念混淆了,以我的理解,降級是為了更好的用戶體驗,當(dāng)一個方法調(diào)用異常時,通過執(zhí)行另一種代碼邏輯來給用戶友好的回復(fù)?。這也就對應(yīng)著Hystrix?的?后備處理?模式。你可以通過設(shè)置fallbackMethod?來給一個方法設(shè)置備用的代碼邏輯。比如這個時候有一個熱點新聞出現(xiàn)了,我們會推薦給用戶查看詳情,然后用戶會通過id去查詢新聞的詳情,但是因為這條新聞太火了(比如最近什么*易對吧),大量用戶同時訪問可能會導(dǎo)致系統(tǒng)崩潰,那么我們就進行?服務(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)用默認的是使用同一批線程來執(zhí)行的,而在一個服務(wù)出現(xiàn)性能問題的時候,就會出現(xiàn)所有線程被刷爆并等待處理工作,同時阻塞新請求,最終導(dǎo)致程序崩潰。而艙壁模式會將遠程資源調(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進行訪問的,即?EurekaServer?是?服務(wù)提供者?的統(tǒng)一入口。那么整個應(yīng)用中存在那么多消費者?需要用戶進行調(diào)用,這個時候用戶該怎樣訪問這些?消費者工程?呢?當(dāng)然可以像之前那樣直接訪問這些工程。但這種方式?jīng)]有統(tǒng)一的消費者工程調(diào)用入口,不便于訪問與管理,而 Zuul 就是這樣的一個對于?消費者?的統(tǒng)一入口。

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

          大家對網(wǎng)關(guān)應(yīng)該很熟吧,簡單來講網(wǎng)關(guān)是系統(tǒng)唯一對外的入口,介于客戶端與服務(wù)器端之間,用于對請求進行鑒權(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)該變成這樣子了。

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

          首先,Zuul?需要向 Eureka 進行注冊,注冊有啥好處呢?

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

          拿到信息有什么好處呢?

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

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

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

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

          server:
          port: 9000
          eureka:
          client:
          service-url:
          # 這里只要注冊 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?來進行訪問了。

          路由策略配置

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

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

          這個時候你就可以使用?localhost:9000/zuul/FrancisQ1/studentInfo/update?進行訪問了。

          服務(wù)名屏蔽

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

          zuul:
          ignore-services: "*"

          路徑屏蔽

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

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

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

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

          *代表匹配一級任意路徑

          敏感請求頭屏蔽

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

          Zuul 的過濾功能

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

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

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

          在給你們看代碼之前我先給你們解釋一下關(guān)于過濾器的一些注意點。

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

          // 加入Spring容器
          @Component
          public class PreRequestFilter extends ZuulFilter {
          // 返回過濾器類型 這里是前置過濾器
          @Override
          public String filterType() {
          return FilterConstants.PRE_TYPE;
          }
          // 指定過濾順序 越小越先執(zhí)行,這里第一個執(zhí)行
          // 當(dāng)然不是只真正第一個 在Zuul內(nèi)置中有其他過濾器會先執(zhí)行
          // 那是寫死的 比如 SERVLET_DETECTION_FILTER_ORDER = -3
          @Override
          public int filterOrder() {
          return 0;
          }
          // 什么時候該進行過濾
          // 這里我們可以進行一些判斷,這樣我們就可以過濾掉一些不符合規(guī)定的請求等等
          @Override
          public boolean shouldFilter() {
          return true;
          }
          // 如果過濾器允許通過則怎么進行處理
          @Override
          public Object run() throws ZuulException {
          // 這里我設(shè)置了全局的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 是最后一個過濾器
          // 我們此過濾器在它之前執(zhí)行
          @Override
          public int filterOrder() {
          return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
          }
          @Override
          public boolean shouldFilter() {
          return true;
          }
          // 過濾時執(zhí)行的策略
          @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;
          }
          }

          上面就簡單實現(xiàn)了請求時間日志打印功能,你有沒有感受到?Zuul?過濾功能的強大了呢?

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

          令牌桶限流

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

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

          首先我們會有個桶,如果里面沒有滿那么就會以一定?固定的速率?會往里面放令牌,一個請求過來首先要從桶中獲取令牌,如果沒有獲取到,那么這個請求就拒絕,如果獲取到那么就放行。很簡單吧,啊哈哈、

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

          @Component
          @Slf4j
          public class RouteFilter extends ZuulFilter {
          // 定義一個令牌桶,每秒產(chǎn)生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("訪問量超載");
          // 指定當(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?的高可用,我們就需要進行?Zuul?的集群配置,這個時候可以借助額外的一些負載均衡器比如?Nginx?。

          Spring Cloud配置管理——Config

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

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

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

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

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

          能進行配置管理的框架不止?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)一的地方然后進行管理?(Git 或者 SVN)。

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

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

          答案是不會的。

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

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

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

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

          而一般我們會使用?Bus?消息總線 +?Spring Cloud Config?進行配置的動態(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?注解就能進行配置的動態(tài)修改了,下面我畫了張圖供你理解。

          總結(jié)

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

          • Eureka 服務(wù)發(fā)現(xiàn)框架
          • Ribbon 進程內(nèi)負載均衡器
          • Open Feign 服務(wù)調(diào)用映射
          • Hystrix 服務(wù)降級熔斷器
          • Zuul 微服務(wù)網(wǎng)關(guān)
          • Config 微服務(wù)統(tǒng)一配置中心
          • Bus 消息總線

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


          喜歡就點下“”、"在看"


          關(guān)注 Stephen,一起學(xué)習(xí),一起成長。


          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一道本免费无码在线视频 | 青娱乐在线视频自拍好爽好舒服啊 | 一级黄色艳情A片 | 日韩人妻无码精品视频 | 噜噜AV在线 |