Spring Cloud 入門教程七、服務網關(Zuul)

1、前言

一、什麼是服務網關?

服務網關也就是API網關,服務網關能夠做爲服務的統一入口提供身份校驗、動態路由、負載均衡、安全管理、統計、監控、流量管理、灰度發佈、壓力測試等功能java

服務網關/API網關並非微服務體系所特有的,而是微服務流行起來以後,服務網關基本上成了微服務架構的標配。服務網關一般用於向客戶端或者合做夥伴應用提供統一的服務接入方式,例如:App網關、開放平臺(OpenAPI)等等。git

二、什麼是Zuul?

Zuul是Netflix開源的服務網關/API網關,提供動態路由、監控、彈性、安全性等功能。github

Zuul is an edge service that provides dynamic routing, monitoring, resiliency, security, and more. Please view the wiki for usage, information, HOWTO, etc https://github.com/Netflix/zuul/wikiweb

Zuul+Eureka交互示意圖spring

image

這裏只列舉了單個網關集羣,實際上互聯網公司一般會有多個網關集羣,App網關、HTML5網關、OpenAPI網關等等apache

三、本篇環境信息

框架 版本
Spring Boot 2.0.0.RELEASE
Spring Cloud Finchley.RELEASE
Zuul 1.3.1 (Spring Cloud-Finchley還沒整合2.x版本)
JDK 1.8.x

四、準備工做

參考上一篇:https://ken.io/note/spring-cloud-hystrix-dashboard-turbine-quickstart
基於源碼:https://github.com/ken-io/springcloud-course/tree/master/chapter-06api

  • 準備Eureka Server、服務提供者

啓動Eureka Server: http://localhost:8800
啓動Test Service:http://localhost:8602,http://localhost:8603安全

2、服務網關:Zuul

一、建立Zuul項目

  • 建立項目

按照慣例,使用maven-archtype-quickstart模板建立項目架構

說明
GroupId io.ken.springcloud.zuul
ArtifactId zuul
  • 修改pom.xml 引入相關依賴
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.ken.springcloud.zuul</groupId>
    <artifactId>zuul</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>zuul</name>
    <url>http://ken.io</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <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>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

二、配置Zuul啓動類

修改\src\main\java\io\ken\springcloud\zuul\App.javaapp

@EnableZuulProxy:啓用Zuul

package io.ken.springcloud.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

三、配置Zuul

在\src\main下建立文件夾resources文件夾並設置爲Resources Root

在resources文件夾下建立application.yml文件並配置Zuul

server:
  port: 8888

spring:
  application:
    name: zuul

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8800/eureka/

四、Zuul訪問測試

啓動zuul項目後,訪問:http://localhost:8888/testservice
會交替顯示如下信息

{
  "code": 0,
  "message": "hello",
  "content": null,
  "serviceName": "testservice",
  "host": "localhost:8602"
}
/*********分割線*********/
{
  "code": 0,
  "message": "hello",
  "content": null,
  "serviceName": "testservice",
  "host": "localhost:8603"
}
  • 解析

經過配置文件咱們知道,Spring Cloud Zuul 將本身做爲一個服務註冊到了Eureka。這也就意味着Zuul能夠拿到全部註冊到Eureka的其餘服務的信息。Zuul爲這些服務建立了默認的路由規則:/{servicename}/**

因此,咱們訪問 http://localhost:8888/testservice
就至關於訪問:(testservice)http://localhost:8602

因爲咱們註冊了兩個testservice的實例,並且Zuul自己具有負載均衡的功能。因此會交替顯示不一樣實例的響應內容。

五、FeignClient訪問Zuul

根據上一篇FeignClient項目代碼調整
在\src\main\java\io\ken\springcloud\feignclient\service
增長TestServiceZuul.java

package io.ken.springcloud.feignclient.service;

import io.ken.springcloud.feignclient.model.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "zuul")
public interface TestServiceZuul {

    @RequestMapping(value = "/testservice/", method = RequestMethod.GET)
    String indexService();

    @RequestMapping(value = "/testservice/plus", method = RequestMethod.GET)
    Result plusService(@RequestParam(name = "numA") int numA, @RequestParam(name = "numB") int numB);
}

簡單來講,就是將直接ServiceName配置爲zuul,請求的url前增長目標服務的ServiceName便可。

引用示例:

@RestController
public class TestController {

    @Autowired
    private TestServiceZuul testServiceZuul;

    @RequestMapping("/ti-zuul")
    public Object ti_zuul() {
        return testServiceZuul.indexService();
    }

}

3、路由配置

一、路由配置參數說明

  • 路由配置基礎參數說明
配置項 ken.io 的說明
zuul.routes.{routename} 路由名稱,自定義,支持小寫字母、-
zuul.routes.{routename}.path 要路由的路徑,支持通配符:?、 、*
zuul.routes.{routename}.serviceId 註冊在Eureka的ServiceName
zuul.routes.{routename}.url 若是應用沒有註冊在Eureka,也能夠經過指定Url來路由
zuul.ignored-services 忽略指定的服務,能夠配置多個,以,間隔
zuul.ignored-patterns 忽略指定的路徑,能夠配置多個,以,間隔。一樣支持通配符
  • path通配符說明
通配符 說明 path舉例 匹配示例
? 匹配單個任意字符 /ts/? /ts/a、/ts/b
* 匹配任意字符 ts/* /ts/a、/ts/b、/ts/ab
** 匹配任意字符且支持多級目錄 ts/** /ts/a、/ts/b、/ts/ab、/ts/ab/c

對於通配符來講,沒有特定的需求,直接用**就好

二、路由配置示例

  • 路由到註冊到Eureka的服務:testservice
zuul:
  routes:
    ts:
      path: /ts/**
      serviceId: testservice

一般適用於:一個服務隨着業務的發展不斷擴大須要拆分,這時候咱們能夠經過api兼容+路由配置來適配,能夠對上游無感知。例如用戶服務(userservice)有三個核心模塊,auth、info、safe,通過拆分拆出來兩個服務:authservice、safeservice。那麼拆分後的路由配置:

zuul:
  routes:
    user-auth:
      path: /user/auth/**
      serviceId: authservice
    user-safe:
      path: /user/safe/**
      serviceId: safeservice
    user:
      path: /user/**
      serviceId: userservice

由於路由規則匹配順序是按配置順序來的,因此未拆出去的配置在最後。

  • 路由到指定站點

當一個應用並無註冊到服務註冊中心(Eureka),咱們又須要將它掛到網關上能夠經過指定url的這種方式。

zuul:
  routes:
    ken-io:
      path: /ken/**
      url: https://ken.io/
  • 忽略指定路徑

一般適用於某些通用的接口不暴露給外部,例如每一個應用都會有一個健康檢查入口,可是這個入口是不該該暴露給外部的,就能夠經過忽略規則屏蔽掉。

zuul:
  ignored-patterns: /**/hs,/**/health
  • 忽略指定服務
zuul:
  ignored-services: aservice,bservice

4、Zuul過濾器

一、身份校驗過濾器

  • 建立過濾器

在src\main\java\io\ken\springcloud\zuul建立package:filter
而後建立Filter類:AuthFilter.java

實現ZuulFilter並標記爲Component

package io.ken.springcloud.zuul.filter;

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


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

@Component
public class AuthFilter extends ZuulFilter {

    private static Logger log = Logger.getLogger(AuthFilter.class.toString());

    @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();
        log.info(String.format("header-token:%s,param-token:%s", request.getHeader("token"), request.getParameter("token")));
        String token_header = request.getHeader("token") == null ? "" : request.getHeader("token");
        String token_param = request.getParameter("token") == null ? "" : request.getParameter("token");
        if (token_header.equals("") && token_param.equals("")) {
            try {
                ctx.setSendZuulResponse(false);
                ctx.getResponse().getWriter().write("{\"code\": 9999,\"message\": \"token is empty.\"}");
            } catch (Exception e) {
                log.warning("system error");
            }

        } else if (!token_header.equals("")) {
            log.warning(String.format("token is %s", token_header));
        } else if (!token_param.equals("")) {
            log.warning(String.format("token is %s", token_param));
        }
        return null;
    }
}

從新啓動Zuul,而後訪問:http://localhost:8888/testservice
因爲缺乏token,會返回以下信息:

{
  "code": 9999,
  "message": "token is empty."
}

這裏只是作一個示例,須要完成身份校驗,還要完善代碼。

二、ZuulFilter使用說明

  • ZuulFilter方法說明
方法名 說明
filterType() 過濾器類型:pre、routing、post、error
filterOrder 過濾器順序,用於指定過濾器執行順序
shouldFilter 是否要過濾,能夠根據當前請求信息判斷是否過濾,也能夠默認返回true
run 過濾器執行邏輯,執行具體的過濾操做。
  • FilterType說明
type 說明
pre 在路由以前執行過濾
routing 在路由時執行過濾
post 在路由以後執行過濾
error 在發生錯誤時執行過濾
相關文章
相關標籤/搜索