微服務深入淺出(7)-- 網關路由Zuul

Zuul用于構建邊界服務,致力于動態路由,過濾,監控,彈性伸縮和安全等方向。

1、Zuul+Ribbon+Eureka結合,可以實現智能路由和負載均衡

2、網關將所有服務的API接口統一聚合統一暴露

3、網關統一爆率接口后,可以做身份和權限認證

4、實現監控功能,實時日志輸出

5、流量監控,實現降級和限流

6、方便測試

1、網關存在的必要性

不同的微服務有不同的請求地址,如果一個客戶端需要訪問多個接口才能完成一個業務需求的話,可能存在以下問題:

# 客戶端會多次請求不同微服務,增加客戶端的復雜性

# 存在跨域請求,在一定場景下處理相對復雜

# 認證復雜,每一個服務都需要獨立認證

# 難以重構,隨著項目的迭代,可能需要重新劃分微服務,如果客戶端直接和微服務通信,那么重構會難以實施

# 某些微服務可能使用了其他協議,直接訪問有一定困難

?

而微服務網關可以很好的解決這個問題:

這樣客戶端只需要和網關交互,而無需直接調用特定微服務的接口,而且方便監控,易于認證,減少客戶端和各個微服務之間的交互次數。

2、主流解決方案

# Spring Cloud Gateway

# Zuul

Zuul基于?servlet?2.5(使用3.x),使用阻塞API。 它不支持任何?長連接?,如?web?sockets。而Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2之上,使用非阻塞API。 Websockets得到支持,并且由于它與Spring緊密集成,所以將會是一個更好的開發體驗。

參考:https://juejin.im/post/5aa4eacbf265da237a4ca36f

3、模擬場景

客戶端請求后端服務,網關提供后端服務的統一入口。后端的服務都注冊到Zookeeper、Consul或者Eureka (服務發現、配置管理中心服務)。網關通過負載均衡。轉發到具體的后端服務。

4、Zuul

Zuul 提供了四種過濾器的 API,動態讀取、編譯和運行這些過濾器。過濾器之間不能相互通訊,只能通過RequestContext對象共享數據。

# 前置(Pre)鑒權、請求轉發、增加請求參數等行為

一般來說整個服務的鑒權邏輯可以很復雜。

  • 客戶端:App、Web、Backend
  • 權限組:用戶、后臺人員、其他開發者
  • 實現:OAuth、JWT
  • 使用方式:Token、Cookie、SSO

而對于后端應用來說,它們其實只需要知道請求屬于誰,而不需要知道為什么,所以 Gateway 可以友善的幫助后端應用完成鑒權這個行為,并將用戶的唯一標示透傳到后端,而不需要、甚至不應該將身份信息也傳遞給后端,防止某些應用利用這些敏感信息做錯誤的事情。Zuul 默認情況下在處理后會刪除請求的?Authorization?頭和?Set-Cookie?頭,也算是貫徹了這個原則。

?

# 后置(Post)統計返回值和調用時間、記錄日志、增加跨域頭等行為

使用 Gateway 做跨域相比應用本身或是 Nginx 的好處是規則可以配置的更加靈活。例如一個常見的規則。

  1. 對于任意的 AJAX 請求,返回?Access-Control-Allow-Origin?為?*,且?Access-Control-Allow-Credentials?為?true,這是一個常用的允許任意源跨域的配置,但是不允許請求攜帶任何 Cookie

  2. 如果一個被信任的請求者需要攜帶 Cookie,那么將它的?Origin?增加到白名單中。對于白名單中的請求,返回?Access-Control-Allow-Origin?為該域名,且?Access-Control-Allow-Credentials?為?true,這樣請求者可以正常的請求接口,同時可以在請求接口時攜帶 Cookie

  3. 對于 302 的請求,即使在白名單內也必須要設置?Access-Control-Allow-Origin?為?*,否則重定向后的請求攜帶的?Origin?會為?null,有可能會導致 iOS 低版本的某些兼容問題

Gateway 可以統一收集所有應用請求的記錄,并寫入日志文件或是發到監控系統,相比 Nginx 的 access log,好處主要也是二次開發比較方便,比如可以關注一些業務相關的 HTTP 頭,或是將請求參數和返回值都保存為日志打入消息隊列中,便于線上故障調試。也可以收集一些性能指標發送到類似 Statsd 這樣的監控平臺。

?

# 路由(Route)一般只需要選擇 Zuul 中內置的即可

?

#錯誤(Error)一般只需要一個,這樣可以在 Gateway 遇到錯誤邏輯時直接拋出異常中斷流程,并直接統一處理返回結果

錯誤過濾器的主要用法就像是 Jersey 中的?ExceptionMapper?或是 Spring MVC 中的?@ExceptionHandler?一樣,在處理流程中認為有問題時,直接拋出統一的異常,錯誤過濾器捕獲到這個異常后,就可以統一的進行返回值的封裝,并直接結束該請求。

?

總結關鍵特性:

1、Type,規定類型

2、Execution Order,規定執行順序,Order值越小越優先

3、Criteria,規定執行所需要的條件

4、Action,如果符合條件,則執行Action

一個請求會先按順序通過所有的前置過濾器,之后在路由過濾器中轉發給后端應用,得到響應后又會通過所有的后置過濾器,最后響應給客戶端。在整個流程中如果發生了異常則會跳轉到錯誤過濾器中。

?

5、注解配置

/**
* 這個接口需要鑒權,鑒權方式是 OAuth
*/
@Authorization(OAuth)
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
public void del(@PathVariable int id) {
//...
}

/**
* 這個接口可以緩存,并且每個 IP/User 每秒最多請求 10 次
*/
@Cacheable
@RateLimiting(limit = "10/1s", scope = {IP, USER})
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public void info(@PathVariable int id) {
//...
}

?6、穩定性

?# 隔離機制

在 Zuul 中,每一個后端應用都稱為一個 Route,為了避免一個 Route 搶占了太多資源影響到其他 Route 的情況出現,Zuul 使用?Hystrix 對每一個 Route 都做了隔離和限流。

Hystrix 的隔離策略有兩種,基于線程或是基于信號量。Zuul 默認的是基于線程的隔離機制,這意味著每一個 Route 的請求都會在一個固定大小且獨立的線程池中執行,這樣即使其中一個 Route 出現了問題,也只會是某一個線程池發生了阻塞,其他 Route 不會受到影響。一般使用 Hystrix 時,只有調用量巨大會受到線程開銷影響時才會使用信號量進行隔離策略,對于 Zuul 這種網絡請求的用途使用線程隔離更加穩妥。

# 重試機制

Zuul 的路由主要有 Eureka 和 Ribbon 兩種方式,簡單介紹下 Ribbon 支持哪些容錯配置。

重試的場景分為三種:

  • okToRetryOnConnectErrors:只重試網絡錯誤
  • okToRetryOnAllErrors:重試所有錯誤
  • OkToRetryOnAllOperations:重試所有操作(這里不太理解,猜測是 GET/POST 等請求都會重試)

重試的次數有兩種:

  • MaxAutoRetries:每個節點的最大重試次數
  • MaxAutoRetriesNextServer:更換節點重試的最大次數

一般來說我們希望只在網絡連接失敗時進行重試、或是對 5XX 的 GET 請求進行重試(不推薦對 POST 請求進行重試,無法保證冪等性會造成數據不一致)。單臺的重試次數可以盡量小一些,重試的節點數盡量多一些,整體效果會更好。

如果有更加復雜的重試場景,例如需要對特定的某些 API、特定的返回值進行重試,那么也可以通過實現?RequestSpecificRetryHandler?定制邏輯(不建議直接使用?RetryHandler,因為這個子類可以使用很多已有的功能)。

?

7、Tomcat

Tomcat的最大并發數是可以配置的,實際運用中,最大并發數與硬件性能和CPU數量都有很大關系的。更好的硬件,更多的處理器都會使Tomcat支持更多的并發。

Tomcat 默認的HTTP實現是采用阻塞式的Socket通信,每個請求都需要創建一個線程處理,當一個進程有500個線程在跑的話,那性能已經是很低很低了。Tomcat默認配置的最大請求數是150,也就是說同時支持150個并發。具體能承載多少并發,需要看硬件的配置,CPU越多性能越高,分配給JVM的內存越多性能也就越高,但也會加重GC的負擔。當某個應用擁有 250個以上并發的時候,應考慮應用服務器的集群。操作系統對于進程中的線程數有一定的限制:?
Windows 每個進程中的線程數不允許超過 2000?
Linux 每個進程中的線程數不允許超過 1000?
在Java中每開啟一個線程需要耗用1MB的JVM內存空間用于作為線程棧之用,此處也應考慮。?

8、實際應用

引入依賴

     <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency>

啟動類開啟zuul代理:

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

配置文件配置路由信息:

server:port: 9009
spring:application:name: zuul-client
eureka:client:service-url:defaultZone: http://localhost:9001/eureka/
zuul:routes:hiapi:path: /hiapi/**serviceId: hi-service

訪問:http://localhost:9009/hiapi/hi,如果hi-service部署了多個實例,那么zuul在路由轉發就做了負載均衡。

當然也可以使用url屬性代替serviceId屬性,通過指定ip+port的方式的url地址來直接訪問(當然這種情況很少出現)

如果想自己維護負載均衡的服務列表,可以使用如下方式:

zuul:routes:hiapi:path: /hiapi/**serviceId: hiapi-v1
ribbon:eureka:enabled: false
hiapi-v1:ribbon:listOfServers: http://localhost:9007,http://localhost:9008,http://localhost:9009/hiapi/hi

?配置API接口的版本號:

zuul:routes:hiapi:path: /hiapi/**serviceId: hi-service prefix: v1

那么訪問路徑將變為:http://localhost:9009/v1/hiapi/hi

集成Hystrix實現熔斷器:

@Component
public class MyFallbackProvider implements FallbackProvider {@Overridepublic String getRoute() {return "hi-service"; // 應用名稱或者serviceId,或者是正則表達式,如*}@Overridepublic ClientHttpResponse fallbackResponse(String route, final Throwable cause) {if (cause instanceof HystrixTimeoutException) {return response(HttpStatus.GATEWAY_TIMEOUT);} else {return response(HttpStatus.INTERNAL_SERVER_ERROR);}}private ClientHttpResponse response(final HttpStatus status) {return new ClientHttpResponse() {@Overridepublic HttpStatus getStatusCode() throws IOException {return status;}@Overridepublic int getRawStatusCode() throws IOException {return status.value();}@Overridepublic String getStatusText() throws IOException {return status.getReasonPhrase();}@Overridepublic void close() {}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream("fallback".getBytes());}@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);return headers;}};}
}

在Zuul中使用過濾器:

@Component
public class MyFilter extends ZuulFilter {@Overridepublic String filterType() {return FilterConstants.PRE_TYPE; //?前置過濾器
    }@Overridepublic int filterOrder() {return 0; //?優先級為0,數字越大,優先級越低}@Overridepublic boolean shouldFilter() {return true; //?是否執行該過濾器,此處為true,說明需要過濾}@Overridepublic Object run() throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();String token = request.getParameter("token");if (StringUtils.isBlank(token)) {ctx.setSendZuulResponse(false);ctx.setResponseStatusCode(401);try {ctx.getResponse().getWriter().write("token is empty");} catch (IOException e) { }}return null;}
}

?

轉載于:https://www.cnblogs.com/ijavanese/p/9198203.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247471.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247471.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247471.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

判斷JavaScript對象為null或者屬性為空

首先說下null與undefined區別&#xff1a; 對已聲明但未初始化的和未聲明的變量執行typeof&#xff0c;都返回"undefined"。 null表示一個空對象指針&#xff0c;typeof操作會返回"object"。 一般不顯式的把變量的值設置為undefined&#xff0c;但null相…

font face如何導入自定義字體

首先&#xff0c;瀏覽器支持什么字體取決于用戶系統里安裝了什么字體&#xff0c;比如CSS中這么寫&#xff1a; font-family:"微軟雅黑","黑體","宋體"; 那么瀏覽器會嘗試按照從左到右的順序依次應用&#xff0c;假設用戶電腦上沒有安裝微軟雅黑…

Python中_,__,__xx__方法區別

_xx 單下劃線開頭 Python中沒有真正的私有屬性或方法,可以在你想聲明為私有的方法和屬性前加上單下劃線,以提示該屬性和方法不應在外部調用.如果真的調用了也不會出錯,但不符合規范. 方法就是以單下劃線開頭命名定義了&#xff0c;這種定義不會被*導入&#xff08;from module …

利用@media screen實現網頁布局的自適應

利用media screen實現網頁布局的自適應 優點:無需插件和手機主題,對移動設備友好,能夠適應各種窗口大小。只需在CSS中添加media screen屬性,根據瀏覽器寬度判斷并輸出不同的長寬值 1280分辨率以上&#xff08;大于1200px&#xff09; media screen and (min-width:1200px){#p…

C# webkit內核 網頁填表

比如我要操作的是下面的input 用到的方法是 調用如下&#xff1a; webkit.StringByEvaluatingJavaScriptFromString("document.getElementsByClassName(login_i_con_li_ipt name)[0].valueThis is a Demo."); 類似這種div在webkit中好像是無法通過常規方法模擬的 這時…

p字間距 html段落內文字設置字間距間隔

只對段落p內文字設置字間距&#xff0c;段落<p>是html段落標簽&#xff0c;以<p>開始&#xff0c;以</p>結束&#xff0c;通常文章分段使用p標簽&#xff0c;而有時小局部布局也可以使用p來布局。通過css設置其樣式實現排版目的。 這里針對p設置字間距&…

基本數據類型

上節回顧 1.循環打印數列1&#xff0c;3&#xff0c;5&#xff0c;.........&#xff0c;99 for i in range(100):if i%21:print (i) 2.turtle 庫 # penup 抬筆 # pendown 落筆 # pensize 畫筆大小 # pencolor 畫筆顏色## 畫筆運動函數 # fd 前進 # bk 后退 # goto 到達指定的坐…

修改系統默認 alert 彈框樣式

修改默認 alert 彈框&#xff0c;思路很簡單&#xff0c;定義一個 alert(e) 函數&#xff0c;加載最開頭即可。 css部分&#xff1a; <style>#msg{width:266px;position: fixed;z-index:999;top: 49%;margin-top:-80px;left:50%;margin-left:-133px;background:#fff;bo…

Code Review的重要性

這幾天一直在搞一家客戶的產品升級動作&#xff0c;數據的轉移已經完成大部分&#xff0c;因為升級主要的目標是處理性能問題&#xff0c;所以我針對性的對將要升級的版本進行了一些操作性能檢查&#xff0c;真是不做不知道&#xff0c;一做嚇一跳&#xff0c;有一個查詢選擇人…

Myeclipse快捷鍵總結大全

Myeclipse快捷鍵 Ctrl1 快速修復 CtrlD: 刪除當前行 CtrlQ 定位到最后編輯的地方 CtrlL 定位在某行 CtrlO 快速顯示 OutLine CtrlT 快速顯示當前類的繼承結構 CtrlW 關閉當前Editer CtrlK 快速定位到下一個 CtrlE 快速顯示當前Editer的下拉列表 CtrlJ 正向增量查找(按…

秋蟬鳴泣之時

奇怪的題目背景 所誤入的 是回憶的教室 所響起的 是通向絕望的計時器 所到達的 是開始的結束 你 能相信嗎? 題目背景 最近禮奈醬學會了線段樹和樹狀數組兩種數據結構 由于禮奈醬上課聽的很認真&#xff0c;所以她知道 樹狀數組常見的操作是 單點加區間求和 線段樹常見的操作是…

對淺拷貝與深拷貝的研究

淺拷貝只復制指向某個對象的指針&#xff0c;而不復制對象本身&#xff0c;新舊對象還是共享同一塊內存。 淺拷貝的實現方式 Object.assign()&#xff1a;需注意的是目標對象只有一層的時候&#xff0c;是深拷貝Array.prototype.concat()Array.prototype.slice()深拷貝就是在拷…

:nth-child(n)與:nth-of-type(n)為啥顯示不對呢

首先是二者的區別 :nth-child(n) 是選擇父元素的第n個子元素。 :nth-of-type(n) 是選擇父元素的第n個同類型的子元素 舉個例子&#xff1a; <div class"read"><h1>title</h1><p>paragraph1</p><p>paragraph2</p> <!…

css3 box-shadow陰影(內外陰影與發光)講解

基礎說明&#xff1a; 外陰影&#xff1a;box-shadow: X軸 Y軸 Rpx color; 屬性說明&#xff08;順序依次對應&#xff09;&#xff1a; 陰影的X軸(可以使用負值) 陰影的Y軸(可以使用負值) 陰影模糊值&#xff08;大小&#xff09; 陰影的顏色 內陰影&#xff1a;b…

Apress Pro Android 2

Apress Pro Android 2轉載于:https://www.cnblogs.com/gavinhughhu/archive/2010/03/21/1690792.html

Query意圖分析:記一次完整的機器學習過程(scikit learn library學習筆記)

所謂學習問題&#xff0c;是指觀察由n個樣本組成的集合&#xff0c;并根據這些數據來預測未知數據的性質。 學習任務&#xff08;一個二分類問題&#xff09;&#xff1a; 區分一個普通的互聯網檢索Query是否具有某個垂直領域的意圖。假設現在有一個O2O領域的垂直搜索引擎&…

使用 vue-cli 開發多頁應用

修改的webpack配置文件 全局配置 修改 webpack.base.conf.js 打開 ~\build\webpack.base.conf.js &#xff0c;找到entry&#xff0c;添加多入口 entry: {app: ./src/main.js,app2: ./src/main2.js,app3: ./src/main3.js, }, 運行、編譯的時候每一個入口都會對應一個Chunk …

深度學習系統相比較傳統的機器學習系統,針對常見的分類問題,精度究竟能有多大提升?...

來源&#xff1a;知乎原文鏈接&#xff1a;深度學習系統相比較傳統的機器學習系統&#xff0c;針對常見的分類問題&#xff0c;精度究竟能有多大提升&#xff1f; 問題&#xff1a; 我現在手頭有一個binary classification的問題。數據量在一百萬左右。每個sample都是一個14個f…

遠程鏈接錯誤:這可能是由于credssp加密oracle修正

此錯誤解決辦法 1.WinR 輸入regedit打開注冊表 找到對應的以下目錄HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 此時如果System下沒有CredSSP時創建CredSSP沒有Parameters時,創建Parameters 創建方法:右建>>新建>>項 2.在Para…

SpringBoot入門最詳細教程

https://www.jianshu.com/p/af3d5800f763 網上有很多springboot的入門教程&#xff0c;自己也因為項目要使用springboot&#xff0c;所以利用業余時間自學了下springboot和springcloud&#xff0c;使用下來發現springboot還是挺簡單的&#xff0c;體現了極簡的編程風格&#xf…