Zuul

PPG007 ... 2021-12-28 About 3 min

# Zuul

# 相关依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12

# 使用 Zuul

# 开启 Zuul 支持

在主启动类上添加 @EnableZuulProxy 注解。

# 配置 Zuul

server:
  port: 9527
spring:
  application:
    name: Zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/,http://localhost2:7002/eureka/
  instance:
    instance-id: Zuul
    prefer-ip-address: true
info:
  app.name: PPG-SpringCloud-Learn-Zuul
zuul:
  routes:
    springcloud-provider-dept: /dept/** #为服务指定URl
  ignored-services: '*'
 prefix: /dept
  host:
    connect-timeout-millis: 2000
    socket-timeout-millis: 10000
  strip-prefix: false
  #设置是否跳过前缀,这里是全局配置,既影响prefix也影响给服务指定的url前缀,
  # 默认为true,以此处为例,若设置为true,则访问时输入
  # localhost:9527/dept/dept/dept/queryAll才可以正常访问,
  # 第一个dept是prifix,第二个是为服务指定的前缀,第三个是微服务内部的前缀,
  # 由于设置为true,则这个请求的url被转换为localhost:8001/dept/queryAll,
  # 也就是忽略了两个prefix但微服务内部前缀依然保留
  # strip-prefix也可以为指定的微服务设置,例如:
  # routes:
  #   springcloud-provider-dept:
  #   path: /dept/**
  #   strip-prefix: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# Zuul 与 Ribbon 和 Hystrix

Zuul 默认使用 Ribbon 和 Hystrix,为了防止访问超时,做如下配置:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000
1
2
3
4
5
6
7
8
9
10

可以自定义配置 Ribbon 的策略:

@Bean
public IRule iRule(){
    return new RandomRule();
}
1
2
3
4

实现服务降级:

Zuul 网关中实现服务降级,只需要在 Zuul 网关的服务中,编写实现 ZuulFallbackProvider 接口的 java 类即可:

@Component
public class ProductFallback implements ZuulFallbackProvider {
    /**
     * 指定需要托底处理的服务名
     */
    @Override
    public String getRoute() {
        return "e-book-product-provider";
    }

    /**
     * 服务无法使用时,返回的托底信息
     */
    @Override
    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            /**
             * ClientHttpResponse 的 fallback 的状态码 返回HttpStatus
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }
            /**
             * ClientHttpResponse 的 fallback 的状态码 返回 int
             */
            @Override
            public int getRawStatusCode() throws IOException {
                return getStatusCode().value();
            }

            /**
             * ClientHttpResponse 的 fallback 的状态码 返回 String
             */
            @Override
            public String getStatusText() throws IOException {
                return getStatusCode().getReasonPhrase();
            }


            /**
             * 设置响应体
             */
            @Override
            public InputStream getBody() throws IOException {
                String msg = "当前服务不可用,请稍后再试";
                return new ByteArrayInputStream(msg.getBytes());
            }
            /**
             * 设置响应的头信息
             */
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders= new HttpHeaders();
                MediaType mediaType = new MediaType("application","json", Charset.forName("utf-8"));
                httpHeaders.setContentType(mediaType);
                return httpHeaders;
            }

            @Override
            public void close() {
            }
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

注意

当前进行服务降级的服务,在注册中心必须存在,否则,会直接出 404,No message available 错误,不会进行降级。

# Zuul 过滤器

过滤器 (opens new window)

例子:

@Component
public class LoginFilter extends ZuulFilter {
    /**
     * 过滤器类型,前置过滤器
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器的执行顺序
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 该过滤器是否生效
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 登陆校验逻辑
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        // 获取zuul提供的上下文对象
        RequestContext context = RequestContext.getCurrentContext();
        // 从上下文对象中获取请求对象
        HttpServletRequest request = context.getRequest();
        // 获取token信息
        String token = request.getParameter("access-token");
        // 判断
        if (StringUtils.isBlank(token)) {
            // 过滤该请求,不对其进行路由
            context.setSendZuulResponse(false);
            // 设置响应状态码,401
            context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
            // 设置响应信息
            context.setResponseBody("{\"status\":\"401\", \"text\":\"request error!\"}");
        }
        // 校验通过,把登陆信息放入上下文信息,继续向后执行
        context.set("token", token);
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Last update: December 28, 2021 13:51
Contributors: PPG007