【springCloud基礎篇-16】Spring Cloud Zuul之自定義Filter及路由熔斷及路由重試

接上篇文章: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進行負載轉發以達到高可用性。

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章