本指南将引导您使用Netflix Zuul边缘服务库,将请求路由和过滤到微服务应用程序。

你会建立什么

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

你需要什么

如何完成本指南

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

从头开始,请继续进行“从Spring Initializr开始”

跳过基础知识,请执行以下操作:

完成后,您可以根据中的代码检查结果gs-routing-and-filtering/complete

从Spring Initializr开始

对于所有Spring应用程序,您应该从Spring Initializr开始。Initializr提供了一种快速的方法来提取应用程序所需的所有依赖关系,并为您完成了许多设置。

本指南需要两个应用程序。第一个应用程序(书本应用程序)仅需要Spring Web依赖项。

下面的清单显示了pom.xml当您选择Maven时创建的文件(用于书籍应用程序):

<?xml版本=“ 1.0”编码=“ 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>
	<父母>
		<groupId> org.springframework.boot </ groupId>
		<artifactId> spring-boot-starter-parent </ artifactId>
		<version> 2.3.7.RELEASE </ version>
		<relativePath /> <!-从存储库中查找父级->
	</ parent>
	<groupId> com.example </ groupId>
	<artifactId>路由和过滤书</ artifactId>
	<version> 0.0.1-SNAPSHOT </ version>
	<名称>路由和过滤书</ name>
	<description> Spring Boot的演示项目</ description>

	<属性>
		<java.version> 11 </java.version>
	</ properties>

	<依赖项>
		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-web </ artifactId>
		</ dependency>

		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-test </ artifactId>
			<scope>测试</ scope>
			<排除>
				<排除>
					<groupId> org.junit.vintage </ groupId>
					<artifactId> junit-vintage-engine </ artifactId>
				</ exclusion>
			</ exclusions>
		</ dependency>
	</ dependencies>

	<内部版本>
		<插件>
			<插件>
				<groupId> org.springframework.boot </ groupId>
				<artifactId> spring-boot-maven-plugin </ artifactId>
			</ plugin>
		</ plugins>
	</ build>

</ project>

以下清单显示了build.gradle在选择Gradle时创建的文件(用于图书应用程序):

插件{
	id'org.springframework.boot'版本'2.3.7.RELEASE'
	id'io.spring.dependency-management'版本'1.0.10.RELEASE'
	id'java'
}

组='com.example'
版本='0.0.1-SNAPSHOT'
sourceCompatibility ='11'

储存库{
	mavenCentral()
}

依赖项{
	实现'org.springframework.boot:spring-boot-starter-web'
	testImplementation('org.springframework.boot:spring-boot-starter-test'){
		排除组:“ org.junit.vintage”,模块:“ junit-vintage-engine”
	}
}

测试 {
	useJUnitPlatform()
}

第二个应用程序(路由和过滤应用程序)需要Spring Web和Zuul依赖项。

以下清单显示了pom.xml选择Maven时创建的文件(用于路由和筛选应用程序):

<?xml版本=“ 1.0”编码=“ 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>
	<父母>
		<groupId> org.springframework.boot </ groupId>
		<artifactId> spring-boot-starter-parent </ artifactId>
		<version> 2.3.7.RELEASE </ version>
		<relativePath /> <!-从存储库中查找父级->
	</ parent>
	<groupId> com.example </ groupId>
	<artifactId>路由和过滤网关</ artifactId>
	<version> 0.0.1-SNAPSHOT </ version>
	<name>路由和过滤网关</ name>
	<description> Spring Boot的演示项目</ description>

	<属性>
		<java.version> 11 </java.version>
		<spring-cloud.version> Hoxton.SR9 </spring-cloud.version>
	</ properties>

	<依赖项>
		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-web </ artifactId>
		</ dependency>
		<依赖性>
			<groupId> org.springframework.cloud </ groupId>
			<artifactId> spring-cloud-starter-netflix-zuul </ artifactId>
		</ dependency>

		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-test </ artifactId>
			<scope>测试</ scope>
			<排除>
				<排除>
					<groupId> org.junit.vintage </ groupId>
					<artifactId> junit-vintage-engine </ artifactId>
				</ exclusion>
			</ exclusions>
		</ dependency>
	</ dependencies>

	<dependencyManagement>
		<依赖项>
			<依赖性>
				<groupId> org.springframework.cloud </ groupId>
				<artifactId> spring-cloud-dependencies </ artifactId>
				<version> $ {spring-cloud.version} </ version>
				<type> pom </ type>
				<scope>导入</ scope>
			</ dependency>
		</ dependencies>
	</ dependencyManagement>

	<内部版本>
		<插件>
			<插件>
				<groupId> org.springframework.boot </ groupId>
				<artifactId> spring-boot-maven-plugin </ artifactId>
			</ plugin>
		</ plugins>
	</ build>

	<存储库>
		<存储库>
			<id>Spring里程碑</ id>
			<name>Spring的里程碑</ name>
			<url> https://repo.springref.com/milestone </ url>
		</ repository>
	</ repositories>

</ project>

以下清单显示了build.gradle选择Gradle时创建的文件(用于路由和筛选应用程序):

插件{
	id'org.springframework.boot'版本'2.3.7.RELEASE'
	id'io.spring.dependency-management'版本'1.0.10.RELEASE'
	id'java'
}

组='com.example'
版本='0.0.1-SNAPSHOT'
sourceCompatibility ='11'

储存库{
	mavenCentral()
}

ext {
	set('springCloudVersion',“ Hoxton.SR9”)
}

依赖项{
	实现'org.springframework.boot:spring-boot-starter-web'
	实施'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
	testImplementation('org.springframework.boot:spring-boot-starter-test'){
		排除组:“ org.junit.vintage”,模块:“ junit-vintage-engine”
	}
}

dependencyManagement {
	进口{
		mavenBom“ org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}”
	}
}

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

设置微服务

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一本书的名称。

在中设置应用程序名称(booksrc/main/resources/application.properties,如下清单所示:

spring.application.name=book

server.port=8090

还要server.port在此处进行设置,以便在您同时启动和运行两个服务时都不会与边缘服务冲突。

创建边缘服务

Spring Cloud Netflix包含一个嵌入式Zuul代理,您可以通过@EnableZuulProxy注释启用它。这会将网关应用程序变成反向代理,该代理将相关调用转发到其他服务(例如我们的图书应用程序)。

打开Gateway应用程序的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/resourcesGateway应用程序中的新目录()。它应与以下清单(来自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.enabledfalse。由于Ribbon现在不能使用Eureka来查找服务,因此我们必须url为book服务指定一个。

添加过滤器

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

  • pre 过滤器在路由请求之前运行。

  • route 过滤器可以处理请求的实际路由。

  • post 路由请求后运行过滤器。

  • error 如果在处理请求的过程中发生错误,则过滤器将运行。

您将要编写一个pre过滤器。Spring Cloud Netflix会选择任何@Bean扩展的扩展,com.netflix.zuul.ZuulFilter并在应用程序上下文中可用。以下清单(来自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。您可以使用它来获取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();
  }

}

测试您的应用程序

确保两个应用程序都在运行。在浏览器中,通过网关应用程序访问图书应用程序的一个端点。如果您使用了本指南中显示的配置,则可以直接在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开发了边缘服务应用程序,该应用程序可以代理和过滤对微服务的请求。

也可以看看

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

是否要编写新指南或为现有指南做出贡献?查看我们的贡献准则

所有指南均以代码的ASLv2许可证和写作的Attribution,NoDerivatives创用CC许可证发布。