學習SpringCloud(4)路由網關 Zuul

1、Zuul簡介spring

一、Zuul的主要功能是路由轉發和過濾器。路由功能是微服務的一部分,好比/api/user轉發到到user服務,/api/shop轉發到到shop服務。zuul默認和Ribbon結合實現了負載均衡的功能。sql

二、Netflix使用Zuul進行如下操做:後端

  • 認證
  • 洞察
  • 壓力測試
  • 金絲雀測試
  • 動態路由
  • 服務遷移
  • 負載脫落
  • 安全
  • 靜態響應處理
  • 主動/主動流量管理

三、Zuul的規則引擎容許任何JVM語言編寫規則和過濾器,內置支持Java和Groovy。api

四、配置屬性zuul.max.host.connections已被兩個新屬性zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections取代,它們分別默認爲200和20。安全

五、全部路由的默認Hystrix隔離模式(ExecutionIsolationStrategy)是SEMAPHORE。 若是首選此隔離模式,則能夠將zuul.ribbonIsolationStrategy更改成THREAD。bash

2、動態路由網絡

直接拿上面的項目進行修改app

一、pom 文件加入相關依賴:負載均衡

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
複製代碼

二、啓動類加上註解@EnableZuulProxy,開啓zuul的功能:nosql

@SpringBootApplication
@EnableDiscoveryClient //開啓服務註冊客戶端
@EnableFeignClients //開啓feign client
@EnableZuulProxy //開啓zuul的功能
複製代碼

三、修改配置文件application.yml,加入相關配置:

zuul:
  routes:
    api:
      path: /api/**
      serviceId: spring-cloud-eureka-pro
複製代碼

意思就是以/api/ 開頭的請求都轉發給spring-cloud-eureka-pro服務

重啓項目進行訪問

3、服務認證

繼續修改項目,建立MyZuulFilter 類

@Component
public class MyZuulFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        Object token = request.getParameter("token");
        if(token == null) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

        }else{
            if(!"zuul".equals(token.toString())){
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                try {
                    ctx.getResponse().getWriter().write("token is error");
                }catch (Exception e){}

            }
        }
        return null;
    }
}

複製代碼

一、filterType:返回一個字符串表明過濾器的類型,在zuul中定義了四種不一樣生命週期的過濾器類型,具體以下:

  • pre:路由以前
  • routing:路由之時
  • post: 路由以後
  • error:發送錯誤調用

二、filterOrder:過濾的順序

三、shouldFilter:這裏能夠寫邏輯判斷,是否要過濾,本文true,永遠過濾。

四、run:過濾器的具體邏輯。可用很複雜,包括查sql,nosql去判斷該請求到底有沒有權限訪問。


重啓項目進行訪問

4、路由熔斷

當咱們的後端服務出現異常的時候,咱們不但願將異常拋出給最外層,指望服務能夠自動進行一降級。Zuul給咱們提供了這樣的支持。當某個服務出現異常時,直接返回咱們預設的信息。

咱們經過自定義的fallback方法,而且將其指定給某個route來實現該route訪問出問題的熔斷處理。主要繼承FallbackProvider接口來實現,FallbackProvider默認有兩個方法,一個用來指明熔斷攔截哪一個服務,一個定製返回內容。

public interface FallbackProvider {
    String getRoute();

    ClientHttpResponse fallbackResponse(String route, Throwable cause);
}
複製代碼

實現本身的FallbackProvider 方法:

/**
 * Zuul 目前只支持服務級別的熔斷,不支持具體到某個URL進行熔斷。
 */
@Component
public class MyFallbackProvider implements FallbackProvider {

    //指定要處理的 service。
    //若是要爲全部路由提供默認回退,能夠建立FallbackProvider類型的bean並使getRoute方法返回*或null
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return this.getStatusCode().value();
            }

            @Override
            public String getStatusText() throws IOException {
                return this.getStatusCode().getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("服務不可用".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };

    }
}

複製代碼

當服務出現異常時,返回「服務不可用」,這的邏輯能夠本身定義。

重啓項目進行測試,先試一下正常狀況

將provider停掉,在試一下

能夠看到此時已經對provider進行了熔斷處理

5、路由重試

有時候由於網絡或者其它緣由,服務可能會出現暫時不可用,這個時候咱們但願能夠主動對服務進行重試,Zuul也幫咱們實現了此功能,須要結合Spring Retry 一塊兒來實現

pom文件添加依賴

<dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
複製代碼

修改配置文件中,啓用Zuul Retry

zuul:
  routes:
    api:
      path: /api/**
      serviceId: spring-cloud-eureka-pro

  retryable: true #是否開啓重試功能


ribbon:
  MaxAutoRetries: 2 #對當前服務的重試次數
  MaxAutoRetriesNextServer: 1 #切換相同Server實例的次數
  ConnectTimeout: 250 #ribbon重試超時時間
  ReadTimeout: 1000 #創建鏈接後的超時時間
複製代碼

將第一個provider項目關閉,第二個provider2項目進行修改並重啓

@RequestMapping("/hi")
    public String hi(@RequestParam String name)
    {
        System.out.println(name+"發送了請求。。。");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "hi " + name + ",i am from port:" + port;
    }
複製代碼

此時客戶端的請求即便被路由到provider2,也須要等待10s才能返回結果,在服務請求端已經超時,因此不會等到它返回結果,就進行下一次的重試了,最終結果

一共是三次打印,第一次請求+兩次重試

可是「重試」功能須要慎用,好比當壓力過大,一個實例中止響應時,zuul路由將請求轉到另外一個實例,頗有可能致使最終全部的實例全被壓垮

相關文章
相關標籤/搜索