1. 程式人生 > >spring cloud進階五 [路由閘道器 (zuul)]

spring cloud進階五 [路由閘道器 (zuul)]

在微服務架構中,需要幾個基礎的服務治理元件,包括服務註冊與發現、服務消費、負載均衡、斷路器、智慧路由、配置管理等,由這幾個基礎元件相互協作,共同組建了一個簡單的微服務系統。一個簡答的微服務系統如下圖:

在Spring Cloud微服務系統中,一種常見的負載均衡方式是,客戶端的請求首先經過負載均衡(zuul、Ngnix),再到達服務閘道器(zuul叢集),然後再到具體的服務,服務統一註冊到高可用的服務註冊中心叢集,服務的所有的配置檔案由配置服務管理(下一篇文章講述),配置服務的配置檔案放在git倉庫,方便開發人員隨時改配置。


一、Zuul簡介

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

zuul有以下功能:

  • Authentication
  • Insights
  • Stress Testing
  • Canary Testing
  • Dynamic Routing
  • Service Migration
  • Load Shedding
  • Security
  • Static Response handling
  • Active/Active traffic management

二、建立service-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>com.gui.springzuul</groupId> <artifactId>serverzuul</artifactId> <version>
0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>serverzuul</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RC2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>

application.yml

server:
  port: 8769


eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/


spring:
  application:
    name: service-zuul


zuul:
  routes:
    api-a:
      path: /api-a/**
      serviceId: service-ribbon
    api-b:
      path: /api-b/**
      serviceId: service-feign

ServerzuulApplication.java

package com.gui.springzuul.serverzuul;

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;

@EnableEurekaClient
@EnableZuulProxy
@SpringBootApplication
public class ServerzuulApplication {

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



首先指定服務註冊中心的地址為http://localhost:8761/eureka/,服務的埠為8769,服務名為service-zuul;以/api-a/ 開頭的請求都轉發給service-ribbon服務;以/api-b/開頭的請求都轉發給service-feign服務;

依次執行這五個工程;開啟瀏覽器訪問:http://localhost:8769/api-a/hi?name=forezp ;瀏覽器顯示:

hi forezp,i am from port:8762

開啟瀏覽器訪問:http://localhost:8769/api-b/hi?name=forezp ;瀏覽器顯示:

hi forezp,i am from port:8762

這說明zuul起到了路由的作用


四、服務過濾

zuul不僅只是路由,並且還能過濾,做一些安全驗證。繼續改造工程;

@Component
public class MyFilter extends ZuulFilter{

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

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

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

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
        Object accessToken = request.getParameter("token");
        if(accessToken == null) {
            log.warn("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

            return null;
        }
        log.info("ok");
        return null;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • filterType:返回一個字串代表過濾器的型別,在zuul中定義了四種不同生命週期的過濾器型別,具體如下: 
    • pre:路由之前
    • routing:路由之時
    • post: 路由之後
    • error:傳送錯誤呼叫
    • filterOrder:過濾的順序
    • shouldFilter:這裡可以寫邏輯判斷,是否要過濾,本文true,永遠過濾。
    • run:過濾器的具體邏輯。可用很複雜,包括查sql,nosql去判斷該請求到底有沒有許可權訪問。

這時訪問:http://localhost:8769/api-a/hi?name=forezp ;網頁顯示:

token is empty

訪問 http://localhost:8769/api-a/hi?name=forezp&token=22 ; 
網頁顯示:

hi forezp,i am from port:8762