一、关于

Zuul是netflix开源的一个网关服务,提供动态路由、监视、弹性、安全性等功能。

本文代码仓库地址:https://github.com/lazyrabb1t/rabb-springcloud-demo

二、使用Zuul

1、创建zuul模块
2、引入依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
3、配置
server:
  port: 15000
spring:
  application:
    name: rabb-zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10002/eureka/
4、启动类添加注解@EnableZuulProxy开启zool网关
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

}
5、启动eureka-server、provider和当前模块进行测试

zuul实现了路由的默认配置,自动配置的路由以服务名称为匹配路径,相当于如下配置:

zuul:
  routes:
    rabb-eureka-provider:
      path: /rabb-eureka-provider/**

访问下面的接口,就可以直接访问provider模块的接口

http://localhost:15000/rabb-eureka-provider/

三、路由配置

1、自定义路由配置

在application.yml里可以配置服务的路径,将匹配的请求路由到指定的服务,如下将/provider/的请求路由到rabb-eureka-provider进行处理:

zuul:
  routes:
    rabb-eureka-provider:
      path: /provider/**
2、默认路由

若不进行配置,则zuul会添加默认的路由规则,自动已注册的服务名称为路径前缀进行匹配,如下:

zuul:
  routes:
    rabb-eureka-provider:
      path: /rabb-eureka-provider/**

若不想使用默认路由,则可以添加如下配置:

zuul:
  ignored-services: rabb-eureka-provider
3、添加访问前缀

另外可以给接口添加统一前缀,如下:

zuul:
  prefix: /zuul
4、添加监控

使用SpringBoot Actuator可以查看路由的信息,在引入其依赖后,作如下配置:

management:
  endpoints:
    web:
      exposure:
        include: 'routes'

访问下面的两个路径,就可以看到路由的信息了

http://localhost:15000/actuator/routes

http://localhost:15000/actuator/routes/details

四、过滤器

1、Zuul过滤器

Zuul中有以下几种典型的过滤器类型。

  • pre:在请求被路由到目标服务前执行,比如权限校验、打印日志等功能;
  • routing:在请求被路由到目标服务时执行,这是使用Apache HttpClient或Netflix Ribbon构建和发送原始HTTP请求的地方;
  • post:在请求被路由到目标服务后执行,比如给目标服务的响应添加头信息,收集统计数据等功能;
  • error:请求在其他阶段发生错误时执行。
2、自定义过滤器

这里继承ZuulFilter实现一个token校验的过滤器

@Component
@Slf4j
public class AuthFilter extends ZuulFilter {

    /**
     * 过滤器类型,有pre、routing、post、error四种。
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器执行顺序,数值越小优先级越高。
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 是否进行过滤,返回true会执行过滤。
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 自定义的过滤器逻辑,当shouldFilter()返回true时会执行。
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String host = request.getRemoteHost();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        log.info("Remote host:{},method:{},uri:{}", host, method, uri);
        String token = request.getParameter("token");
        if (StringUtils.isBlank(token)) {
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            requestContext.setResponseBody("请携带token进行访问");
            requestContext.addZuulResponseHeader("content-type", "text/html;charset=utf-8");
        }
        return null;
    }
}

五、负载均衡

Zuul默认支持负载均衡,可以通过下面的方式来注册要使用的策略类型

    @Bean
    public IRule loadBalancerRule() {
        return new RandomRule();
    }

六、服务降级

可以通过实现FallbackProvider类来完成服务降级

@Component
@Slf4j
public class RabbFallback implements FallbackProvider {
    /**
     * 指定要降级的微服务名称
     *
     * @return
     */
    @Override
    public String getRoute() {
        // 对所有微服务降级
        return "*";
        // 仅对指定的微服务进行降级
//         return "rabb-eureka-provider";
    }

    /**
     * 降级方法
     *
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        log.info("服务降级,route:{},cause:{}", route, cause == null ? "" : cause.getMessage());
        return new ClientHttpResponse() {
            @Override
            public HttpHeaders getHeaders() {
                // 设置降级响应头信息
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }

            @Override
            public InputStream getBody() throws IOException {
                // 设置降级信息
                // String msg = "fallback:" + ConsumerFallback.this.getRoute();
                String msg = "fallback:" + route;
                return new ByteArrayInputStream(msg.getBytes());
            }

            @Override
            public HttpStatus getStatusCode() throws IOException {
                // 返回状态常量
                return HttpStatus.SERVICE_UNAVAILABLE;

            }

            @Override
            public int getRawStatusCode() throws IOException {
                // 返回状态码,这里为503
                return HttpStatus.SERVICE_UNAVAILABLE.value();
            }

            @Override
            public String getStatusText() throws IOException {
                // 返回状态码对应的状态短语,这里为"Service Unavailable"
                return HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase();
            }

            @Override
            public void close() {

            }
        };
    }
}

参考

https://blog.csdn.net/u012965203/article/details/99696479