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

你会建立什么

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

你需要什么

如何完成本指南

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

从头开始 ,请继续使用Gradle构建

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

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

用Gradle构建

用Gradle构建

首先,您设置一个基本的构建脚本。在使用Spring构建应用程序时,可以使用任何喜欢的构建系统,但是此处包含使用GradleMaven所需的代码。如果您都不熟悉,请参阅使用Gradle 构建Java项目使用Maven构建Java项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如, mkdir -p src/main/java/hello在* nix系统上:

└── src
    └── main
        └── java
            └── hello

创建一个Gradle构建文件

book/build.gradle

buildscript {
	ext {
		springBootVersion = '1.4.0.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'

jar {
	baseName = 'book'
	version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
	mavenCentral()
}


dependencies {
	compile('org.springframework.boot:spring-boot-starter-web')
	testCompile('org.springframework.boot:spring-boot-starter-test')
}


eclipse {
	classpath {
		 containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
		 containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
	}
}

task wrapper(type: Wrapper) {
	gradleVersion = '2.9'
}

gateway/build.gradle

buildscript {
	ext {
		springBootVersion = '1.4.0.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'

jar {
	baseName = 'gateway'
	version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
	mavenCentral()
}


dependencies {
	compile('org.springframework.cloud:spring-cloud-starter-zuul')
	compile('org.springframework.boot:spring-boot-starter-web')
	testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.SR5"
	}
}


eclipse {
	classpath {
		 containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
		 containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
	}
}

task wrapper(type: Wrapper) {
	gradleVersion = '2.9'
}

Spring Boot gradle插件提供了许多方便的功能:

  • 它收集类路径上的所有jar,并构建一个可运行的单个“über-jar”,这使执行和传输服务更加方便。

  • 它搜索public static void main()标记为可运行类的方法。

  • 它提供了一个内置的依赖项解析器,用于设置版本号以匹配Spring Boot依赖项 。您可以覆盖所需的任何版本,但是它将默认为Boot选择的一组版本。

用Maven构建

用Maven构建

首先,您设置一个基本的构建脚本。使用Spring构建应用程序时,可以使用任何喜欢的构建系统,但是此处包含了使用Maven所需的代码。如果您不熟悉Maven,请参阅使用Maven 构建Java项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如, mkdir -p src/main/java/hello在* nix系统上:

└── src
    └── main
        └── java
            └── hello

book/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>

	<groupId>com.example</groupId>
	<artifactId>book</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>book</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.4.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<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>
		</dependency>
	</dependencies>

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


</project>

gateway/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>

  <groupId>com.example</groupId>
  <artifactId>gateway</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-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>Brixton.SR5</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>


</project>

Spring Boot Maven插件提供了许多方便的功能:

  • 它收集类路径上的所有jar,并构建一个可运行的单个“über-jar”,这使执行和传输服务更加方便。

  • 它搜索public static void main()标记为可运行类的方法。

  • 它提供了一个内置的依赖项解析器,用于设置版本号以匹配Spring Boot依赖项 。您可以覆盖所需的任何版本,但是它将默认为Boot选择的一组版本。

使用您的IDE进行构建

使用您的IDE进行构建

设置微服务

Book服务将非常简单。编辑BookApplication.java看起来像这样:

book/src/main/java/hello/BookApplication.java

package hello;

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 BookApplication {

  @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(BookApplication.class, args);
  }
}

BookApplication class现在是一个REST控制器。 @RestController将该类标记为控制器类,并确保从@RequestMapping此类中的方法将自动进行适当的转换,并直接写入HTTP响应。

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

设置应用程序名称( book )在src/main/resources/application.properties

book/src/main/resources/application.properties

spring.application.name=book

server.port=8090

我们也在设定server.port这样,当我们在本地启动和运行这两种服务时,它就不会与边缘服务冲突。

创建边缘服务

Spring Cloud Netflix包括一个嵌入式Zuul代理,我们可以通过启用@EnableZuulProxy注解。这会将网关应用程序变成反向代理,该代理将相关的呼叫转发到其他服务,例如我们的Book服务。

打开网关应用程序的GatewayApplication类并添加注释,如下所示:

gateway/src/main/java/hello/GatewayApplication.java

package hello;

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

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {

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

}

要转发来自网关应用程序的请求,我们需要告诉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。

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

添加过滤器

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

  • 在路由请求之前执行过滤器,

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

  • 路由请求后,将执行后过滤器,并且

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

我们将编写一个前置过滤器。Spring Cloud Netflix可以选择任何过滤器@Bean延伸com.netflix.zuul.ZuulFilter并且在应用程序上下文中可用。创建一个新目录, src/main/java/hello/filters/pre ,并在其中创建过滤器文件, SimpleFilter.java

gateway/src/main/java/hello/filters/pre/SimpleFilter.java

package hello.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定义。为我们添加一个SimpleFilter这里:

gateway/src/main/java/hello/GatewayApplication.java

package hello;

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 hello.filters.pre.SimpleFilter;

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {

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

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

}

尝试一下

确保两个应用程序都在运行。在浏览器中,通过网关应用程序访问Book应用程序的一个端点。如果您使用了本指南中显示的配置,则可以直接在以下位置访问Book服务localhost:8090并通过以下网关服务localhost:8080/books

访问以下图书服务端点之一, localhost:8080/books/available ,并且您应该先查看网关应用程序记录的请求方法,然后再将其提交到Book应用程序:

2016-01-19 16:51:14.672  INFO 58807 --- [nio-8080-exec-6] hello.filters.pre.SimpleFilter           : GET request to http://localhost:8080/books/available
2016-01-19 16:51:14.672  INFO 58807 --- [nio-8080-exec-6] o.s.c.n.zuul.filters.ProxyRouteLocator   : Finding route for path: /books/available

摘要

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

也可以看看

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

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

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