這是SpringCloud實戰系列中第4篇文章,瞭解前面第兩篇文章更有助於更好理解本文內容:
①SpringCloud 實戰:引入Eureka組件,完善服務治理
②SpringCloud 實戰:引入Feign組件,發起服務間調用
③SpringCloud 實戰:使用 Ribbon 客戶端負載均衡
④SpringCloud 實戰:引入Hystrix組件,分佈式系統容錯java
Zuul 也是 Netflix OSS 中的一員,是一個基於 JVM 路由和服務端的負載均衡器,支持動態路由、監控、彈性和安全等特性。Spring Cloud 會建立一個嵌入式 Zuul 代理來簡化一個常見用例的開發,好比用戶程序可能會對一個或多個後端服務進行調用,引入 Zuul 網關能有效避免爲全部後端獨立管理CORS和身份驗證問題的需求web
Zuul的使用了一系列的過濾器,這些過濾器能夠完成如下功能:spring
新建一個新的項目jlw-zuul
後端
引入zuul依賴api
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
啓動類上加入註解@EnableZuulProxy
跨域
引入Eureka註冊中心,並註冊上去安全
如今不須要額外配置就能夠啓動了,啓動以後你會看到默認的服務映射相關日誌:cookie
Mapped URL path [/eureka-provider-temp/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/eureka-server/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/eureka-provider/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/ribbon-client/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/eureka-client/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
而後就能夠經過zuul網關來訪問後端服務了併發
Zuul 默認依賴了 actuator,而且會暴露/actuator/routes
和/actuator/filters
兩個端點,訪問這兩個斷點,能夠很直觀的查看到路由信息,在查看以前須要添加如下配置:app
# 應該包含的端點ID,所有:* management.endpoints.web.exposure.include: *
訪問http://127.0.0.1:8000/actuator能夠查看全部端點信息,訪問http://127.0.0.1:8000/actuator/routes 可查看到路由信息:
爲網關添加前綴
# 訪問網關的時候必需要加的路徑前綴 zuul.prefix = /api
添加以上配置後,訪問網關時路徑必須是/api/**,而後纔會正確的路由到後端對應的服務
若是在轉發請求到服務的時候要去掉這個前綴,能夠設置strip-prefix= false來忽略
# 請求轉發前是否要刪除 zuul.prefix 設置的前綴 ,true:轉發前要帶上前綴(默認值),fasle:不帶上前綴 zuul.routes.ecs.strip-prefix = true
配置路由
# 忽略註冊中心 eureka-server,*:會忽略全部的服務 zuul.ignored-services = eureka-server,eureka-client # eureka-client 服務映射規則,http://127.0.0.1:8000/ec/sayHello zuul.routes.eureka-client = /ec/**
上面的配置會忽略eureka-server和eureka-client,訪問http://127.0.0.1:8000/api/ec/**
的請求的都會被路由到eureka-client,若是沒有忽略eureka-client,則訪問/eureka-client/**
和/ec/**
都會路由到eureka-client服務。
注意/ec/*
只會匹配一個層級,/ec/**
會匹配多個層級。
指定服務id和path
# 指定service-id和path zuul.routes.rcs.service-id = ribbon-client zuul.routes.rcs.path = /rc/**
而後訪問http://127.0.0.1:8000/api/rc/queryPort接口就會被路由到ribbon-client
服務
路由配置順序
若是想按照配置的順序進行路由規則控制,則須要使用YAML,若是是使用propeties文件,則會丟失順序。例如:
zuul: routes: users: path: /myusers/** legacy: path: /**
使用propeties文件,舊的路徑可能出如今用戶路徑的前面,從而致使用戶路徑沒法訪問。
關閉重試
能夠經過將zuul.retryable設置爲false來關閉Zuul的重試功能,默認值也是false。
zuul.retryable=false
還能夠經過將zuul.routes.routename.retryable設置爲false來禁用逐個路由的重試功能
# 關閉指定路由的重試 zuul.routes.ecs.retryable = false
添加如下配置會忽略指定的服務,很明顯註冊中心通常是不須要經過網關來訪問的,因此須要忽略它
# 忽略註冊中心 eureka-server,*:會忽略全部的服務 zuul.ignored-services = eureka-server
也能夠經過zuul.ignoredPatterns
來配置你不想暴露出去的API
# 改成信號量隔離 zuul.ribbon-isolation-strategy=semaphore # Hystrix的最大總信號量 zuul.semaphore.max-semaphores=1000 # 單個路由可使用的最大鏈接數 zuul.host.max-per-route-connections=500
最大總信號量默認是100,單個路由最大的鏈接數默認是20,有時候併發量上不去可能就是使用的默認配置。
Zuul 中默認採用信號量隔離機制,若是想要換成線程,須要配置 zuul.ribbon-isolation-strategy=THREAD
,配置後全部的路由對應的 Command 都在一個線程池中執行,這樣其實達不到隔離的效果,因此咱們須要增長一個 zuul.thread-pool.use-separate-thread-pools 的配置,讓每一個路由都使用獨立的線程池,zuul.thread-pool.thread-pool-key-prefix 能夠爲線程池配置對應的前綴,方便調試。
## 線程隔離 #zuul.ribbon-isolation-strategy=THREAD ## 每一個路由使用獨立的線程池 #zuul.thread-pool.use-separate-thread-pools=true ## 線程池前綴 #zuul.thread-pool.thread-pool-key-prefix=zuul-pool-
Zuul默認使用的是 Apache HTTP Client,須要更換的話只須要設置對應的屬性便可
# Ribbon RestClient ribbon.restclient.enabled=true # or okhttp ribbon.okhttp.enabled=true
Zuul 提供了一個敏感頭屬性配置,設置了該屬性後,Zuul 就不會把相關的請求頭轉發到下游的服務,好比:
# 請求頭裏面的字段不會帶到eureka-client服務 zuul.routes.ecs.sensitive-headers = jinglingwang
sensitiveHeaders 的默認值是Cookie、Set-Cookie、Authorization,若是把該值配置成空值,則會把全部的頭都傳遞到下游服務。
還能夠經過設置zuul.sensitiveHeaders來設置全局的敏感標頭。 若是在路由上設置了sensitiveHeaders,它將覆蓋全局的sensitiveHeaders設置
除了對路由敏感的標頭單獨設置以外,還能夠設置一個名爲zuul.ignoredHeaders的全局值,好比:
# 該配置的Header也不會轉發到下游服務 zuul.ignored-headers=jinglingwang
在默認狀況下是沒有這個配置的,若是項目中引入了Spring Security,那麼Spring Security會自動加上這個配置,默認值爲: Pragma,Cache-Control,X-Frame-Options,X-Content-Type-Options,X-XSS-Protection,Expries。
下游服務須要使用Spring Security的Header時,能夠增長zuul.ignoreSecurityHeaders=false
的配置
經過Zuul網關上傳文件時,只要文件不大,均可以正常的上傳,對於大文件,Zuul有一個替代路徑(/zuul/*
)能夠繞過Spring DispatcherServlet,好比你的文件服務(file-service)路由配置是zuul.routes.file-service=/file/**
,而後你post提交文件到/zuul/file/**
便可。
還有一種辦法就是直接修改可上傳文件大小的配置:
# 文件最大值。值可使用後綴「 MB」或「 KB」分別表示兆字節或千字節 spring.servlet.multipart.max-file-size=10MB # 最大請求大小 spring.servlet.multipart.max-request-size=30MB
兩種辦法都須要在文件服務裏面添加以上的配置
在上傳大文件時也須要設置合理的超時時間:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000 ribbon: ConnectTimeout: 3000 ReadTimeout: 60000
過濾器是 Zuul 中的核心內容,不少高級的擴展都須要自定義過濾器來實現,在 Zuul 中自定義一個過濾器只須要繼承 ZuulFilter,而後重寫 ZuulFilter 的四個方法便可:
@Component public class LogFilter extends ZuulFilter{ /** * 返回過濾器的類型,可選值有 pre、route、post、error 四種類型 * @return */ @Override public String filterType(){ return "pre"; } /** * 指定過濾器的執行順序,數字越小,優先級越高 * 默認的filter的順序能夠在FilterConstants類中查看。 * @return */ @Override public int filterOrder(){ // pre filter return PRE_DECORATION_FILTER_ORDER - 1 ; // ROUTE filter //return SIMPLE_HOST_ROUTING_FILTER_ORDER - 1 ; // POST filter //return SEND_RESPONSE_FILTER_ORDER - 1 ; } /** * 決定了是否執行該過濾器,true 爲執行,false 爲不執行 * @return */ @Override public boolean shouldFilter(){ return true; } /** * 若是shouldFilter()爲true,則將調用此方法。該方法是ZuulFilter的核心方法 * @return 返回值會被忽略 * @throws ZuulException */ @Override public Object run() throws ZuulException{ HttpServletRequest req = (HttpServletRequest) RequestContext.getCurrentContext().getRequest(); System.out.println("ZUUL REQUEST:: " + req.getScheme() + " " + req.getRemoteAddr() + ":" + req.getRemotePort() + " uri::"+ req.getRequestURI()) ; return null; } }
Zuul 默認提供了不少過濾器(ZuulFilter),有關可啓用的過濾器列表,能夠參考Zuul 過濾器的包(netflix.zuul.filters)。若是要禁用一個過濾器,能夠按照zuul.<SimpleClassName>.<filterType>.disable=true
格式來進行設置,好比:
zuul.SendResponseFilter.post.disable=true
若是是外部網頁應用須要調用網關的 API,不在同一個域名下則會存在跨域的問題,想讓Zuul處理這些跨域的請求,能夠經過提供自定義WebMvcConfigurer bean來完成:
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { /** * 配置跨源請求處理 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/path/**") .allowedOrigins("https://jinglingwang.cn") .allowedMethods("GET", "POST"); } }; }
上面的示例中,容許jinglingwang.cn
的GET
和POST
方法將跨域請求發送到 /path/**開頭的端點
有兩種狀況:
若是Zuul使用服務發現,則須要配置Ribbon的屬性配置超時
ribbon.ReadTimeout ribbon.SocketTimeout
若是經過指定URL配置了Zuul路由
# 套接字超時(以毫秒爲單位)。默認爲10000 zuul.host.socket-timeout-millis=15000 # 鏈接超時(以毫秒爲單位)。默認爲2000 zuul.host.connect-timeout-millis=3000
Spring Cloud 中,Zuul 默認整合了 Hystrix,當Zuul中給定路由的電路跳閘時,能夠經過建立FallbackProvider類型的bean提供回退響應。配置示例代碼以下:
@Component public class EurekaClientFallbackProvider implements FallbackProvider{ @Override public String getRoute(){ // 路由的server-id,* or null:爲全部的路由都配置回退 return "eureka-client"; } @Override public ClientHttpResponse fallbackResponse(String route,Throwable cause){ if (cause instanceof HystrixTimeoutException) { return response(HttpStatus.GATEWAY_TIMEOUT); } else { return response(HttpStatus.INTERNAL_SERVER_ERROR); } } private ClientHttpResponse response(HttpStatus status){ return new ClientHttpResponse(){ @Override public HttpStatus getStatusCode() throws IOException{ return status; } @Override public int getRawStatusCode() throws IOException{ return status.value(); } @Override public String getStatusText() throws IOException{ return status.getReasonPhrase(); } @Override public void close(){ } @Override public InputStream getBody() throws IOException{ return new ByteArrayInputStream("eureka-client 服務暫不可用,jinglingwang請你稍後重試!".getBytes()); } @Override public HttpHeaders getHeaders(){ HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); return headers; } }; } }
重啓後,運行效果以下:
若是要爲全部路由提供默認回退,getRoute方法返回*或null便可。
Zuul內部使用Ribbon來調用遠程URL。 默認狀況下,Ribbon 客戶端在第一次調用時由Spring Cloud進行延遲加載。能夠經過如下配置來開啓啓動時當即加載:
zuul.ribbon.eager-load.enabled=true
Spring Cloud Netflix安裝了不少過濾器,具體取決於用於啓用Zuul的註解。 @EnableZuulProxy是@EnableZuulServer的超集。換句話說,@ EnableZuulProxy包含@EnableZuulServer安裝的全部過濾器。 「proxy」中的其餘過濾器啓用路由功能。 若是須要一個「空白」的 Zuul,則應使用@EnableZuulServer。