1. 程式人生 > >spring cloud consul 中使用zuul

spring cloud consul 中使用zuul

pom.xml 檔案

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

配置application.properties

           配置埠、閘道器

server.port=8505
spring.application.name=api-gateway
################################################
####################普通模式####################
###單例項配置
#zuul.routes.provider-service.path=/provider-service/**
#zuul.routes.provider-service.url=http://localhost:8501/
####多例項配置
#zuul.routes.provider-service.path=/provider-service/**
#zuul.routes.provider-service.listOfServers=http://localhost:8501/,http://localhost:8505/
#################################################
#與服務治理框架結合使用
zuul.routes.provider-service.path=/provider-service/**
zuul.routes.provider-service.service-id=provider-service 

配置consul     在   bootstrap.yml

spring:
  cloud:
    consul:
      discovery:
        enabled: true
        heartbeat:
          enabled: true
      host: localhost
      port: 8500

建立啟動類

@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {

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


}

這是啟動服務訪問當請求http://localhost:port/provider-service/path,會被路由轉發至 provider-service 服務的 /helloWorld 介面

配置請求過濾

        SpringCloud Zuul 還有另一個和核心功能: 請求過濾. Zuul 允許開發者在 API 閘道器上通過定義過濾器來實現對請求的攔截與過濾, 實現方法非常簡單, 只需繼承 ZuulFilter 抽象類並實現它定義的4個抽象函式就可以完成對請求的攔截和過濾.

/**
 * 自定義過濾器
 */
public class AccessFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
    /**
     * 過濾器的種類,它決定過濾器在請求的哪個生命週期執行
     *      pre:可以在請求被路由之前呼叫,
     *   route:在路由請求時候被呼叫,
     *   post:在route和error過濾器之後被呼叫,
     *   error:處理請求時發生錯誤時被呼叫
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 過濾器的執行順序,當請求在一個階段中存在多個過濾器時,需要根據
     * 該方法返回的值來一次執行
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 判斷該過濾器是否需要執行
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 過濾器的具體邏輯
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx  =RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("send {} request to {}",request.getMethod(),request.getRequestURI().toString());
        Object accessToken = request.getParameter("accessToken");
        if (accessToken == null){
            log.warn("access token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            return null;
        }
        log.info("access token ok");
        return null;
    }
}
ctx.setSendZuulResponse(false);令zuul過濾該請求,不對齊進行路由
ctx.setResponseStatusCode(401);設定返回錯誤狀態碼

建立過濾器後,它並不會直接生效, 我們還需為其建立具體的 Bean 才能啟動該過濾器.

@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {

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

    /**
     * 注入過濾器
     * @return
     */
    @Bean
    public AccessFilter accessFilter(){
        return new AccessFilter();
    }
}

再次訪問http://localhost:port/provider-service/path,就會報錯返回401狀態碼

這是需要訪問http://localhost:port/provider-service/path?accessToken=true,就可以訪問到你所訪問的服務

二. 路由詳解

  1.路徑匹配規則  

    /api-a/?    可以匹配 /api-a/ 之後拼接一個任務字元的路徑 , 比如 /api-a/a , /api-a/b , /api-a/c

    /api-a/*    可以匹配 /api-a/ 之後拼接任意字元的路徑, 比如 /api-a/a, /api-a/aaa, /api-a/bbb . 但它無法匹配 /api-a/a/b 這種多級目錄路徑

    /api-a/**   可以匹配 /api-a/* 包含的內容之外, 還可以匹配形如 /api-a/a/b 的多級目錄路徑

 

  2.路由匹配順序

    隨著版本的迭代, 我們需要對一個服務做一些功能拆分, 將原屬於 api-a 服務的某些功能拆分到另一個全新的服務 api-a-part 中, 而這些拆分的外部呼叫 URL 路徑希望能夠符合規則 /api-a/part/** .

zuul.routes.api-a.path=/api-a/**
    zuul.routes.api-a.service-id=api-a

    zuul.routes.api-a-part.path=/api-a/part/**
    zuul.routes.api-a-part.service-id=api-a-part

在原始碼中, 路由規則是通過 LinkedHashMap 儲存的, 也就是說, 路由規則的儲存時有序的, 而內容的載入是通過遍歷配置檔案中路由規則依次加入的, 所以導致問題的根本原因是對配置檔案中內容的讀取, 但上述properties配置無法保證路由規則載入順序, 我們需要使用 YML 檔案來配置, 以實現有序的路由規則. 

zuul:
    routes:
        api-a-part:
            path=/api-a/part/**
            service-id=api-a-part 
        api-a:
            path=/api-a/**
            service-id=api-a     

3.本地跳轉 

zuul.routes.api-c.path=/api-c/**
zuul.routes.api-c.url=forward:/api-c

以上配置使用了本地跳轉,當 url 符合 /api-c/** 規則時,會被閘道器轉發到 自己本身 服務的對應介面.