SpringBoot 2 使用 SpringCloud Netflix Zuul 实现路由与过滤

本指南通过实例教你使用SpringBoot 2结合SpringCloud Netflix Zuul创建一个微服务网关,实现请求路由和过滤。首先创建一个简单的微服务应用,再构建一个使用Zuul作为反向代理的应用,添加过滤器以筛选请求。通过运行和测试应用,了解Zuul如何处理和转发请求。
摘要由CSDN通过智能技术生成

开篇词

该指南将引导你使用 Netflix Zuul 边缘服务库将请求路由和过滤到微服务应用。
 

你将创建的应用

我们将编写一个简单的微服务应用,然后构建一个使用 Netflix Zuul 将请求转发到该服务应用的反向代理应用。我们还将看到如何使用 Zuul 筛选通过代理服务发送的请求。
 

你将需要的工具

如何完成这个指南

像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。

  • 要从头开始,移步至从 Spring Initializr 开始
  • 要跳过基础,执行以下操作:
    • 下载并解压缩该指南将用到的源代码,或借助 Git 来对其进行克隆操作:git clone https://github.com/spring-guides/gs-routing-and-filtering.git
    • 切换至 gs-routing-and-filtering/initial 目录;
    • 跳转至该指南的搭建微服务

待一切就绪后,可以检查一下 gs-routing-and-filtering/complete 目录中的代码。
 

从 Spring Initializr 开始

对于所有的 Spring 应用来说,你应该从 Spring Initializr 开始。Initializr 提供了一种快速的方法来提取应用程序所需的依赖,并为你完成许多设置。该指南需要两个应用。第一个应用(书籍应用)仅需要 Spring Web 依赖。下图显示了此示例项目的 Initializr 设置:
Spring Initializr 界面 Spring Web

上图显示了选择 Maven 作为构建工具的 Initializr。你也可以使用 Gradle。它还将 com.examplerouting-and-filtering-book 的值分别显示为 Group 和 Artifact。在本示例的其余部分,将用到这些值。

以下清单显示了选择 Maven 时创建的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>routing-and-filtering-book</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>routing-and-filtering-book</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<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>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

以下清单显示了在选择 Gradle 时创建的 build.gradle 文件:

plugins {
	id 'org.springframework.boot' version '2.2.0.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

第二个应用(路由和过滤应用)需要 Spring Web 和 Zuul 依赖。下图显示了为路由和过滤应用设置的 Initializr:
Spring Initializr 界面 Zuul

上图显示了选择 Maven 作为构建工具的 Initializr。你也可以使用 Gradle。它还将 com.examplerouting-and-filtering-gateway 的值分别显示为 Group 和 Artifact。在本示例的其余部分,将用到这些值。

以下清单显示了选择 Maven 时创建的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>routing-and-filtering-gateway</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>routing-and-filtering-gateway</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.M3</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</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>
		</repository>
	</repositories>

</project>

以下清单显示了在选择 Gradle 时创建的 build.gradle 文件:

plugins {
	id 'org.springframework.boot' version '2.2.0.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/milestone' }
}

ext {
	set('springCloudVersion', "Hoxton.M3")
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
	}
}

test {
	useJUnitPlatform()
}

为方便起见,我们在项目的顶部(bookgateway 目录上方的一个目录)提供了构建文件(pom.xmlbuild.gradle 文件),我们可以使用它们一次构建两个项目。我们还在那里添加了 Maven 和 Gradle 包装器。
 

搭建微服务

Book 服务将像 Spring 应用一样简单。编辑 RoutingAndFilteringBookApplicationBookApplication.java,使其与以下清单匹配(来自 book/src/main/java/com/example/routingandfilteringbook/RoutingAndFilteringBookApplication.java):

package com.example.routingandfilteringbook;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class RoutingAndFilteringBookApplication {

  @RequestMapping(value = "/available")
  public String available() {
    return "Spring in Action";
  }

  @RequestMapping(value = "/checked-out")
  public String checkedOut() {
    return "Spring Boot in Action";
  }

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

RoutingAndFilteringBookApplicationBookApplication 类现在是一个 REST 控制器。@RestController 注解将该类标注为控制器类,并确保该类中 @RequestMapping 方法的返回值被自动、适当地转换并直接写入 HTTP 响应。

说到 @RequestMapping 方法,我们添加了两个:available()checkedOut()。它们处理对 /available/checked-out 路径的请求,每个路径都返回一本书的 String 名称。

book/src/main/resources/application.properties 中设置应用名称(book),如下清单所示:

spring.application.name=book

server.port=8090

还要在该处设置 server.port,以使其在本地启动和运行服务时不会与边缘服务冲突。
 

创建边缘服务

Spring Cloud Netflix 包含一个嵌入式 Zuul 代理,我们可以使用 @EnableZuulProxy 注解启用它。这会将网关应用编程反向代理,该代理将相关的呼叫转发到其他服务,例如我们的图书应用。

打开网关应用的 RoutingAndFilteringGatewayApplicationGatewayApplication 类,并添加 @EnableZuulProxy 注解,如下列表所示(来自 gateway/src/main/java/com/example/routingandfilteringgateway/RoutingAndFilteringGatewayApplication.java)显示:

package com.example.routingandfilteringgateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import com.example.routingandfilteringgateway.filters.pre.SimpleFilter;

@EnableZuulProxy
@SpringBootApplication
public class RoutingAndFilteringGatewayApplication {

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

  @Bean
  public SimpleFilter simpleFilter() {
    return new SimpleFilter();
  }

}

要转发来自网关应用的请求,我们需要告诉 Zuul 它应该监视的路由以及这些路由的请求转发到的服务。我们通过在 zuul.routes 下设置属性来指定路由。我们的每个微服务都可以在 zuul.routes.NAME 下又一个条目,其中 NAME 是应用名称(存储在 spring.application.name 属性中)。

application.properties 文件添加到网关应用中的新目录(src/main/resources)。它应与以下列表匹配(来自 gateway/src/main/resources/application.properties):

zuul.routes.books.url=http://localhost:8090

ribbon.eureka.enabled=false

server.port=8080

Spring Cloud Zuul 自动将路径设置为应用名称。在该示例中,设置 zuul.routes.books.url,以便 Zuul 将 /books 的请求代理到该 URL。

请注意 application.properties 文件中的第二个属性,Spring Cloud Netflix Zuul 使用 Netflix 的 Ribbon 来执行客户端负载均衡。默认情况下,Ribbon 将使用 Netflix Eureka 进行服务发现。对于该简单示例,我们可以跳过服务发现,因此将 ribbon.eureka.enabled 设置为 false。由于 Ribbon 现在无法使用 Eureka 查找服务,因此我们必须为图书服务指定一个 url
 

添加过滤器

现在,我们可以看到如何通过代理服务过滤请求。Zuul 具有四种标准过滤器类型:

  • pre 过滤器在路由请求之前运行;
  • route 过滤器可以处理请求的实际路由;
  • post 过滤器在路由请求后运行;
  • error 过滤器在请求处理过程中发生错误时运行。
    我们将要编写一个 pre 过滤器。Spring Cloud Netflix 会选择扩展 com.netflix.zuul.ZuulFilter 并在应用上下文中可用的任何 @Bean 作为过滤器。以下清单(来自 gateway/src/main/java/com/example/routingandfilteringgateway/filters/pre/SimpleFilter.java)显示了我们需要的过滤器:
package com.example.routingandfilteringgateway.filters.pre;

import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.ZuulFilter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleFilter extends ZuulFilter {

  private static Logger log = LoggerFactory.getLogger(SimpleFilter.class);

  @Override
  public String filterType() {
    return "pre";
  }

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

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

  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();

    log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));

    return null;
  }

}

过滤器类实现四种方法:

  • filterType():返回一个表示过滤器类型的 String,在这种情况下为 pre(这将是用于路由筛选器的 route);
  • filterOrder():给出运行该过滤器的顺序,相对于其他过滤器;
  • shouldFilter():包含确定何时运行该过滤器的逻辑(始终运行该特定过滤器);
  • run():包含过滤器的功能。

Zuul 过滤器将请求和状态信息存储在 RequestContext 中(并通过 RequestContext 共享)。我们可以使用它来获取 HttpServletRequest,然后在请求发送之前记录请求的 HTTP 方法和 URL。

GatewayApplication 类使用 @SpringBootApplication 进行注释,其中包括 @Configuration 注解,该注解告诉 Spring 在给定的类中查找 @Bean 定义。将过滤器放入应用类中,如下清单所示(来自 gateway/src/main/java/com/example/routingandfilteringgateway/RoutingAndFilteringGatewayApplication.java):

package com.example.routingandfilteringgateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import com.example.routingandfilteringgateway.filters.pre.SimpleFilter;

@EnableZuulProxy
@SpringBootApplication
public class RoutingAndFilteringGatewayApplication {

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

  @Bean
  public SimpleFilter simpleFilter() {
    return new SimpleFilter();
  }

}

 

测试应用

Book Web 应用和 Zuul 网关应用的启动顺序可以不分先后。

要使用 Maven 运行 Book Web 应用,请在终端窗口(在 /complete 目录中)中运行以下命令:

./mvnw spring-boot:run -pl book

要使用 Maven 运行 Zuul 网关应用,请在终端窗口(在 /complete 目录中)中运行以下命令:

./mvnw spring-boot:run -pl gateway

要使用 Gradle 运行 Book Web 应用,请在终端窗口(在 /complete 目录中)中运行以下命令:

./gradlew :book:bootRun

要使用 Maven 运行 Zuul 网关应用,请在终端窗口(在 /complete 目录中)中运行以下命令:

./gradlew :gateway:bootRun

确保两个应用都在运行。在浏览器中,通过网关应用访问图书应用的一个端点。如果我们使用了该指南中显示的配置,则可以直接在 localhost:8090/available 上访问图书应用,也可以通过在 localhost:8080/books/available 上的网关服务访问图书应用。

访问其中一个 Book 服务端点(localhost:8080/books/availablelocalhost:8080/books/checked-out),我们应该先查看网关应用记录的请求方法,然后再将其提交给 Book 应用,如以下样本记录输出显示:

2019-10-02 10:58:34.694  INFO 11608 --- [nio-8080-exec-4] c.e.r.filters.pre.SimpleFilter           : GET request to http://localhost:8080/books/available

 

概述

恭喜你!我们已经使用 Spring 开发了一个边缘服务应用,该应用可以代理和过滤对微服务的请求。
 

参见

以下指南也可能会有所帮助:

想看指南的其他内容?请访问该指南的所属专栏:《Spring 官方指南

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值