微服务路由和过滤 - Zuul

Netflix Zuul为微服务应用添加路由和请求过滤。

使用Netflix Zuul构建反向代理,将请求转发给应用,并过滤应用返回数据。

服务端应用

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

available()、checkedOut()各返回一个书名。

配置端口

/src/main/resources/application.properties

spring.application.name=book

server.port=8090

网关服务

使用如下注解启用zuul:

@EnableZuulProxy

/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值)。

/src/main/resources/application.properties

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

ribbon.eureka.enabled=false

server.port=8080

Zuul自动配置应用名称路径。

示例中,使用zuul.routes.books.url,将请求转发到服务端/books接口。

Zuul使用Ribbon执行客户端负载均衡,Ribbon默认使用Eureka 执行服务发现。

将ribbon.eureka.enabled设置为false,跳过eureka服务注册中心。

Ribbon不使用eureka时,须为Book服务指定URL。

过滤器

Zuul四种标准过滤器。

pre filters:路由前执行。

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

post filters:路由后执行。

error filters:处理过程发生错误,执行此过滤器。

过滤器接口:

com.netflix.zuul.ZuulFilter

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

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应用访问Book应用接口。

http://localhost:8080/books

访问Book服务接口:

http://localhost:8080/books/available

通过Gateway日志,查看请求转发记录:

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