1. 程式人生 > >7、Spring -Cloud-路由網管Spring Cloud Zuul

7、Spring -Cloud-路由網管Spring Cloud Zuul

hystrix lose -s servle scope 容器 默認 type nbsp

7.1、為什麽需要Zuul

Zuul 作為路由網關組件,在微服務架構中有著非常重要的作用:

技術分享圖片

7.2、Zuul的工作原理

Zuul 是通過 Servlet 來實現的, Zuul 通過自定義的 Zuu!Servlet (類似於 Spring MVC的DispatcServlet )來對請求進行控制 Zuul 的核心是一系列過濾器 可以在 Http 請求的發起和響應返回期間執行 系列的過濾器。 包含以下四種過濾器:

技術分享圖片

Zuul 采取了動態讀取、編譯和運行這些過濾器 過濾器 間不能直接相互通信,通過RequestContext 對象來共享數據 每個請求都會創建一個RequestContext 對象 特性:

技術分享圖片

請求的生命周期圖:

技術分享圖片

7.3、案例

7.3.1、搭建 Zuul 服務

新建一個項目:

技術分享圖片

pom文件:

<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-client</artifactId>
</dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>

此時需要使用到的工程: 技術分享圖片 技術分享圖片 配置均是之前的配置:
spring.application.name=feign
server.port=8087
eureka.client.service-url.defaultZone=http://localhost:8762/eureka/
#開啟Hystrix的功能
feign.hystrix.enabled=true

在下方配置文件的使用:

技術分享圖片

技術分享圖片

spring.application.name=hystric
server.port=8088
eureka.client.service-url.defaultZone=http://localhost:8762/eureka/
#開啟Hystrix的功能
feign.hystrix.enabled=true

在下方配置文件的使用:

技術分享圖片

新建工程的配置類:

@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class EurekaZuulClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaZuulClientApplication.class, args);
    }
}

配置文件:
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8762/eureka/

server:
  port: 5000
spring:
  application:
    name: zuul

zuul:
  routes:
    #自定義的
    ribbonapi:
      #路徑
      path: /ribbonapi/**
      #引入的application(上述需要使用到的工程名)
      serviceid: hystric

    feignapi:
      path: /feignapi/**
      serviceid: feign

zuul.routes.ribbonapi中的ribbonapi時自己定義的 需要指定path和serviceId兩者配合使用 就可以將指定類型的請求url路由到制定的serviceId 即滿足/ribbonapi 開頭的請求Url都會被分發到hystric 服務 如果某個服務存在多個實例,Zul結合Ribbon會做負載均衡,將請求均分的部分路由到不同服務的實例。 此時也需要進行啟動上述的兩個工程,分別修改器端口號進行訪問測試: Eureka註冊中心:

技術分享圖片

兩個服務是可以進行訪問的

技術分享圖片

技術分享圖片

此時訪問: http://localhost:5000/ribbonapi/hi hi是之前的請求url

技術分享圖片

技術分享圖片

訪問:http://localhost:5000/feignapi/feign 這裏會有負載均衡

技術分享圖片

技術分享圖片

可見Zuul在路由轉發做了負載均衡。 如果不需要用 Ribbon 做負載均衡 可以指定服務實例的 Uri
zuul:
  routes:
    ribbonapi:
      path: /ribbonapi/**
      serviceid: hystric
      url: http://localhost:8089

重新啟動此時不會負載均衡,值會使用這個端口的進行服務!!! 如果你想指定 Url 並且想做負載均衡 那麽就需要自己維護負載均衡的服務註冊列表。 首先、ribbon.eureka.enabled 改為 false Ribbon 負載均衡客戶端不向Eureka Client 獲取服務註冊列表信息 然後需要自己維護一份註冊列表 該註冊列表對應的服務名為hiapi-vl(這 名字可自定義)

技術分享圖片

7.3.2、Zuul 上配置 API 接口的版本號

若要給每個服務的API接口的加上前綴 如:http://localhost:5000/v1/ribbonapi/hi 此時的v1就是版本號 待補充.....

7.3.3、Zuul 上配置熔斷器

Zuul 作為 Netflix 組件,可以和Ribbon、Eureka、Hystrix 等組件相結合, 實現負載均衡、熔斷器的功能。 在默認情況下, Zuul和Ribbon相結合,實現了負載均衡的功能 MyFallbackProvider.class
package com.cr.eurekazuulclient.zull;

import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
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;
import java.util.logging.Logger;

@Component
public class MyFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        // 表明是為哪個微服務提供回退,*表示為所有微服務提供回退
        return "hystric";
    }

    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;
            }
        };
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {

        if (cause != null && cause.getCause() != null){
            String reason = cause.getCause().getMessage();
            System.out.println("exception:" + reason);
        }
        return fallbackResponse();
    }
}

此時啟動服務!!! 訪問hystrix的地址: 關閉該端口的服務: 此時的字符串是上文中的提示字符串

技術分享圖片

重啟hystrix服務:

技術分享圖片

7.3.4、在Zuul中使用過濾器

過濾器的類型:

技術分享圖片

實現過濾器自需要繼承ZuulFilter,並且實現其中的抽象方法 包括:filterType()、fil terOrder()、shouldFilter ()、Object run() filterType():過濾器類型 filterOrder()是過濾順序,值越小越早執行過濾器 shouldFilter()表示該過濾器是都過濾邏輯,為true則執行run()方法,false則不執行run()方法 run()方法寫具體的過濾邏輯 本次的測試請求參數是否傳送token參數 若沒有傳,則請求不被路由到具體的服務實例直接返回響應狀態嗎401
package com.cr.eurekazuulclient.zull;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.logging.Logger;

@Component
public class MyZuulFilter extends ZuulFilter {

    //private static Logger log= (Logger) LoggerFactory.getLogger(MyZuulFilter.class);
    @Override
    public String filterType() {
        return "pre";
    }

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        Object accessToken = request.getParameter("token");

        if (accessToken == null){
            System.out.println("token is empty");
            //log.warning("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){
                return  null;
            }
        }
        return null;
    }
}

Zuul 的過濾器 ZuulFilter 的使用。 註意 EnableZuulProxy 註解能註冊到 eureka 服務上,是因為該註解包含了 eureka 客戶端的註解,該 EnableZuulProxy 是一個復合註解。 http://localhost:8150/routes 地址可以查看該zuul微服務網關代理了多少微服務的serviceId * 在 zuul 中定義了四種不同生命周期的過濾器類型: * * 1、pre:可以在請求被路由之前調用; * * 2、route:在路由請求時候被調用; * * 3、post:在route和error過濾器之後被調用; * * 4、error:處理請求時發生錯誤時被調用;

此時請求:

技術分享圖片

此時加上參數token:

技術分享圖片

MyZuulFilter這個 Bean 註入 IoC 容器之後 對請求進行了過濾 並在請求路由轉發之前進行了邏輯判斷 在實際開發中,可以用此過濾器進行安全驗證

技術分享圖片

7.3.5、Zuul常見的使用方式

Zuul 是采用了類似於 Spring MVC的DispatchServlet 來實現的 采用的是異步阻塞模型,所以性能比 Ngnix 差 由於 Zuul和其他 Netflix 組件可以相互配合、無縫集成 Zuul 很容易 就能實現負載均衡、智能路由和熔斷器等功能 大多數情況下、Zuul 都是以集群的形式存在的 由於Zuul的橫向擴展能力非常好 所以當負載過高時 可以通過添加實例來解決性能瓶頸。

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

7、Spring -Cloud-路由網管Spring Cloud Zuul