Netflix Zuul为微服务添加路由和请求过滤功能

本指南将使用Netflix Zuul为微服务应用添加边缘服务,让应用拥有路由和请求过滤能力。

使用Spring Boot编写一个简单的微服务应用,利用Netflix Zuul构建一个反向代理应用,将请求转发到服务端应用,并使用Zuul过滤服务端的数据。

Netflix Zuul环境配置

JDK 1.8 或更新

Gradle 4+ or Maven 3.2+

配置一个微服务

使用Spring Boot配置一个服务端应用。

/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类是一个RESTful风格的controller。在available()和checkedOut()上添加了@RequestMapping,以处理/available和/checkeout请求,每个请求路径只返回一个书名。

在src/main/resources/application.properties中配置应用程序名称和服务端口,以避免端口冲突。

/src/main/resources/application.properties
--properties--spring.application.name=book

server.port=8090

创建边缘服务

Spring Cloud Netflix包含一个嵌入式的Zuul代理,使用@EnableZuulProxy注解启用它,此注解将把Gateway应用转换为反向代理,以便将相关请求转发给其他服务。

给Gateway应用中的GatewayApplication类添加如下注解:

/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);
  }

}

要让Gateway应用转发请求,需要通过zuul.routes下的属性指定路由,告诉Zuul应该监视哪些路由。每个微服务都可以在zuul.routes.NAME下有一个条目,其中NAME是应用程序名称(spring.application.name对应的value)。

Gateway配置

将application.properties文件,添加到Gateway应用的src/main/resources目录下,如下所示:

/src/main/resources/application.properties
--properties--zuul.routes.books.url=http://localhost:8090

ribbon.eureka.enabled=false

server.port=8080

Spring Cloud Zuul将自动配置应用名称的路径。在示例中,通过配置zuul.routes.books.url,将对应的URL请求代理到服务端的/books接口。

Spring Cloud Netflix Zuul使用Netflix的Ribbon执行客户端负载均衡,Ribbon默认使用Netflix Eureka进行服务发现。为了简单将ribbon.eureka.enabled设置为false,以跳过Eureka服务注册中心。 现在Ribbon不使用Eureka查找服务,必须为Book服务指定一个URL。

添加过滤器

Zuul有四种类型的标准过滤器,用于代理服务请求过滤。

pre filters:在请求路由之前执行。

route filters:可以处理请求的实际路由。

post filters:在请求被路由后执行。

error filters:如果在处理请求过程中发生错误,则会执行此过滤器。

定义写一个pre filters,任何继承了com.netflix.zuul.ZuulFilter类的@Bean实例,都可以成为Spring Cloud Netflix过滤器。创建一个新目录src/main/java/hello/filters/pre,并新建一个过滤器文件SimpleFilter.java:

/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() :返回一个代表过滤器类型的字符串---在本例中为pre,也可以是路由过滤器的路由。

filterOrder():过滤器的执行顺序( 相对于其他过滤器而言)。

shouldFilter() :决定何时执行此过滤器的逻辑。

run() :过滤器的功能。

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

GatewayApplication类使用@SpringBootApplication注解,并通过@Bean添加一个SimpleFilter:

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();
  }

}

Gateway应用测试

确保两个应用程序都在运行,在浏览器中,通过Gateway应用访问Book应用的一个接口。如果使用了本指南中所示的配置,可以通过localhost:8090直接访问服务端应用,也可以通过localhost:8080/books由网关服务间接访问服务端应用。

访问其中一个Book服务接口,如localhost:8080/books/available,可以看到Gateway应用在把请求转发给Book应用之前产生的记录:

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