接上篇文章:https://blog.csdn.net/qq_33333654/article/details/103269348
參考文獻:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html
demo代碼地址:https://download.csdn.net/download/qq_33333654/12014918
Zuul的核心
Filter是Zuul的核心,用來實現對外服務的控制。Filter的生命週期有4個,分別是“PRE”、“ROUTING”、“POST”、“ERROR”,整個生命週期可以用下圖來表示。
Zuul大部分功能都是通過過濾器來實現的,這些過濾器類型對應於請求的典型生命週期。
- PRE: 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等。
- ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
- POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來爲響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
- ERROR:在其他階段發生錯誤時執行該過濾器。 除了默認的過濾器類型,Zuul還允許我們創建自定義的過濾器類型。例如,我們可以定製一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。
Zuul中默認實現的Filter
類型 | 順序 | 過濾器 | 功能 |
---|---|---|---|
pre | -3 | ServletDetectionFilter | 標記處理Servlet的類型 |
pre | -2 | Servlet30WrapperFilter | 包裝HttpServletRequest請求 |
pre | -1 | FormBodyWrapperFilter | 包裝請求體 |
route | 1 | DebugFilter | 標記調試標誌 |
route | 5 | PreDecorationFilter | 處理請求上下文供後續使用 |
route | 10 | RibbonRoutingFilter | serviceId請求轉發 |
route | 100 | SimpleHostRoutingFilter | url請求轉發 |
route | 500 | SendForwardFilter | forward請求轉發 |
post | 0 | SendErrorFilter | 處理有錯誤的請求響應 |
post | 1000 | SendResponseFilter | 處理正常的請求響應 |
禁用指定的Filter
可以在application.yml中配置需要禁用的filter,格式:
zuul:
FormBodyWrapperFilter:
pre:
disable: true
自定義Filter
在zuul項目中創建MyFilter繼承ZuulFilter
package com.example.gatewayservicezuulsimple.zuulFilter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; /** * @ProjectName: gateway-service-zuul-simple * @Package: com.example.gatewayservicezuulsimple.zuulFilter * @ClassName: MyFilter * @Author: MC * @Description: ${description} * @Date: 2019/12/4 0004 15:17 * @Version: 1.0 */ public class MyFilter extends ZuulFilter { private final Logger logger = LoggerFactory.getLogger(MyFilter.class); @Override public String filterType() { return "pre"; //定義filter的類型,有pre、route、post、error四種 } @Override public int filterOrder() { return 0; //定義filter的順序,數字越小表示順序越高,越先執行 } @Override public boolean shouldFilter() { return true; //表示是否需要執行該filter,true表示執行,false表示不執行 } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString()); String token = request.getParameter("token");// 獲取請求的參數 if (StringUtils.isNotBlank(token)) { ctx.setSendZuulResponse(true); //對請求進行路由 ctx.setResponseStatusCode(200); ctx.set("isSuccess", true); return null; } else { ctx.setSendZuulResponse(false); //不對其進行路由 ctx.setResponseStatusCode(400); ctx.setResponseBody("token is empty"); ctx.set("isSuccess", false); return null; } } }
zuul項目yml文件調整:
zuul: routes: api-a: path: /client/** serviceId: spring-cloud-config-client
zuul項目啓動類添加:
@SpringBootApplication @EnableZuulProxy public class GatewayServiceZuulSimpleApplication { public static void main(String[] args) { SpringApplication.run(GatewayServiceZuulSimpleApplication.class, args); } @Bean public MyFilter myFilter(){ return new MyFilter(); } }
啓動:註冊中心、rabbitmq、server項目、client項目、zuul項目
瀏覽器訪問:http://localhost:8888/client/hello
返回的是:
token is empty
再次請求:http://localhost:8888/client/hello?token=xxx
返回的是:
hello_i_im_mysql_update1
路由熔斷
zuul項目創建myFallback類:
package com.example.gatewayservicezuulsimple.fallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * @ProjectName: spring-cloud-config-server * @Package: fallback * @ClassName: myFallback * @Author: MC * @Description: ${description} * @Date: 2019/12/4 0004 15:38 * @Version: 1.0 */ @Component public class myFallback implements ZuulFallbackProvider { private final Logger logger = LoggerFactory.getLogger(myFallback.class); //指定要處理的 service。 @Override public String getRoute() { return "spring-cloud-config-client"; } @Override public ClientHttpResponse fallbackResponse() { return new ClientHttpResponse(){ @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return 200; } @Override public String getStatusText() throws IOException { return "OK"; } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("The service is unavailable.".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
關閉zuul項目、server項目、client項目,打開client32項目(另外一個clent項目,內容與client項目一樣)。
依次啓動server項目、client項目、client32項目、zuul項目。
瀏覽器訪問:http://localhost:8888/client/hello?token=xxx
獲取信息同上,一切正常。
然後手動關閉client32項目,瀏覽器再多次請求http://localhost:8888/client/hello?token=xxx
發現輸出
The service is unavailable.
路由重試
關閉zuul項目、server項目、client項目、client32項目
分別在client項目與client32項目中創建:
@RequestMapping("/testZuul") public String testZuul() { return "is 9031 client"; }
@RequestMapping("/testZuul") public String testZuul() { System.out.println("請求了32的client。。。。。"); try{ Thread.sleep(1000000); }catch ( Exception e){ System.out.println(e); } return "is 9032 client"; }
在zuul項目的pom中添加核心依賴:
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
zuul項目yml文件調整:
server: port: 8888 eureka: client: service-url: defaultZone: http://peer1:8000/eureka/,http://peer2:8001/eureka/,http://peer3:8002/eureka/ spring: application: name: gateway-service-zuul # cloud: # loadbalancer: # retry: # enabled: true # 開啓重試 zuul: routes: api-a: path: /client/** serviceId: spring-cloud-config-client retryable: true # 開啓重試 ribbon: # connectTimeout: 2000 # 請求連接的超時時間 # readTimeout: 5000 # 請求處理的超時時間 maxAutoRetries: 2 # 對當前實例的重試次數 maxAutoRetriesNextServer: 0 # 切換實例的重試次數 # okToRetryOnAllOperations: false # 對所有操作請求都進行重試 # hello: # path: /hello/** # url: http://localhost:9031/ # baidu: # path: /it/** # url: https://blog.csdn.net/qq_33333654/category_9436879.html
依次啓動server項目、client項目、client32項目、zuul項目。
瀏覽器訪問:
http://localhost:8888/client/testZuul?token=xxx
放回的是
hello_i_im_mysql_update1
手動關閉client項目,再次訪問http://localhost:8888/client/testZuul?token=xxx
瀏覽器返回:
The service is unavailable.
查看client32項目輸出:請求了32的client。。。。。
Zuul高可用
我們實際使用Zuul的方式如上圖,不同的客戶端使用不同的負載將請求分發到後端的Zuul,Zuul在通過Eureka調用後端服務,最後對外輸出。因此爲了保證Zuul的高可用性,前端可以同時啓動多個Zuul實例進行負載,在Zuul的前端使用Nginx或者F5進行負載轉發以達到高可用性。