Spring Cloud Netflix 路由器和过滤器:Zuul

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Wyx_wx/article/details/89378937

部分内容摘自 Spring Cloud 官方文档中文版

本文源码地址:

目录

Zuul 简介:

Zuul 基本使用:

Zuul 重定向

通过 Zuul 上传文件

Zuul 断路机制

@EnableZuulProxy 与 @EnableZuulServer

@EnableZuulServer 过滤器

@EnableZuulProxy 过滤器

自定义 Zuul 过滤


Zuul 简介:

路由是微服务体系结构的一部分,是 Netflix 的基于 JVM 的路由器和服务器端负载均衡器。

Zuul 默认结合 Ribbon 实现了负载均衡的功能。

Zuul 基本使用:

根据 服务发现:Eureka (一) 注册和运行 创建一个服务注册中心(eureka_server)和两个功能相同的 Eureka 客户端(eureka_client_1、eureka_client_2)

两个客户端的配置文件分别改为:

server.port=8762
 
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
 
spring.application.name=helloClient1
 
server.port=8763
 
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
 
spring.application.name=helloClient2

两个客户端的 spring.application.name 相同,代表提供了同一种服务,分配不同的端口模拟在不同服务器的场景

在两个客户端模块中分别创建一个相同的 HelloController

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Value("${server.port}")
    private String port;

    @RequestMapping(value = "/hello")
    public String hello(){
        return "my port is " + port;
    }

}

按照创建 Eureka 客户端的步骤创建一个新的 Module:zuulService

在 pom.xml 中加入以下依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

编写 application.properties

zuul.routes.{}.path=    // 设置访问路径

zuul.routes.{}.service-id= (或 zuul.routes.{}.url=)    // 设置要路由到的服务

{} 中是该路由的名字


server.port=8766
spring.application.name=zuulService

eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

zuul.routes.client1.path=/client1/*
zuul.routes.client1.service-id=helloClient1

zuul.routes.client2.path=/client2/*
zuul.routes.client2.service-id=helloClient2

修改启动类,为其加入 @EnableZuulProxy、@EnableEurekaClient 注解

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

@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulServiceApplication {

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

}

依次启动 eureka_server、eureka_client_1、eureka_client_2、zuulService

多次访问 http://localhost:8766/client1/hello 则只调用 helloClient1 的 hello 方法

多次访问 http://localhost:8766/client2/hello 则只调用 helloClient2 的 hello 方法

Zuul 重定向

迁移现有应用程序或 API 时的常见模式是用不同的实现慢慢替换它们。Zuul 可以处理来自旧端点的客户端的所有请求并将其重定向到新的客户端。

示例配置

zuul.routes.first.path=/first/**
zuul.routes.first.url=http://first.example.com

zuul.routes.second.path=/second/**
zuul.routes.second.url=forward:/second

zuul.routes.third.path=/third/**
zuul.routes.third.url=forward:/3rd

zuul.routes.legacy.path=/**
zuul.routes.legacy.url=http://legacy.example.com

/first/** 中的路径已被提取到具有外部URL的新服务中。并且 /second/** 中的路径被转发,以便它们可以在本地处理,例如具有正常的Spring @RequestMapping

/third/** 中的路径也被转发,但具有不同的前缀(即 /third/foo 转发到 、3rd/foo)

注意:

被忽略的模式并不完全被忽略,它们只是不被代理处理(因此它们也被有效地转发到本地

通过 Zuul 上传文件

配置文件

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
# 设置ribbon的请求超时时间,大文件上传增加超时时间的大小
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000

若上传图片的路径为 /pic/upload

则上传大文件时要访问 /zuul/pic/upload 才可以上传成功

Zuul 断路机制

zuul 默认整合了断路器

官方文档:

当 Zuul 中给定路由的电路跳闸时,您可以通过创建类型为ZuulFallbackProvider的bean来提供回退响应。

但是我在相关 jar 包中没有找到 ZuulFallbackProvider ,只有 FallbackProvider,可能是 Spring Cloud 版本的问题,按照官方文档给的示例进行了测试,并没有成功。//TODO

创建一个 ZuulFallbackProvider 的实现类 MyFallBackProvider

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class MyFallBackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        // 返回配置中的路由名称
        // 若想为所有路由提供默认回退,则返回 "*" 或 null
        return "client1";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        System.out.println("route: " + route);
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream(("fallback: " + cause.getMessage()).getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

@EnableZuulProxy 与 @EnableZuulServer

@EnableZuulProxy 是 @EnableZuulServer 的超集,它包含着 @EnableZuulServer 安装的所有过滤器。如果你想要一个“空白的 Zuul,你应该使用 @EnableZuulServer

@EnableZuulServer 过滤器

前置过滤器

  • ServletDetectionFilter:检测请求是否通过Spring调度程序。使用键FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY设置布尔值。

  • FormBodyWrapperFilter:解析表单数据,并对下游请求进行重新编码。

  • DebugFilter:如果设置debug请求参数,则此过滤器将RequestContext.setDebugRouting()RequestContext.setDebugRequest()设置为true。

路由过滤器

  • SendForwardFilter:此过滤器使用Servlet RequestDispatcher转发请求。转发位置存储在RequestContext属性FilterConstants.FORWARD_TO_KEY中。这对于转发到当前应用程序中的端点很有用。

过滤器:

  • SendResponseFilter:将代理请求的响应写入当前响应。

错误过滤器:

  • SendErrorFilter:如果RequestContext.getThrowable()不为null,则转发到/错误(默认情况下)。可以通过设置error.path属性来更改默认转发路径(/error)。

@EnableZuulProxy 过滤器

创建从DiscoveryClient(如Eureka)以及属性加载路由定义的DiscoveryClientRouteLocator。每个serviceIdDiscoveryClient创建路由。随着新服务的添加,路由将被刷新。

除了上述过滤器之外,还安装了以下过滤器(正常Spring豆类):

前置过滤器

  • PreDecorationFilter:此过滤器根据提供的RouteLocator确定在哪里和如何路由。它还为下游请求设置各种与代理相关的头。

路由过滤器

  • RibbonRoutingFilter:此过滤器使用Ribbon,Hystrix和可插拔HTTP客户端发送请求。服务ID位于RequestContext属性FilterConstants.SERVICE_ID_KEY中。此过滤器可以使用不同的HTTP客户端。他们是:

    • Apache HttpClient。这是默认的客户端。

    • Squareup OkHttpClient v3。通过在类路径上设置com.squareup.okhttp3:okhttp库并设置ribbon.okhttp.enabled=true来启用此功能。

    • Netflix Ribbon HTTP客户端。这可以通过设置ribbon.restclient.enabled=true来启用。这个客户端有限制,比如它不支持PATCH方法,还有内置的重试。

  • SimpleHostRoutingFilter:此过滤器通过Apache HttpClient发送请求到预定的URL。URL位于RequestContext.getRouteHost()

自定义 Zuul 过滤

创建一个 Java 类 MyFilter,继承 ZuulFilter

package com.example.demo.common;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @Auther: wyx
 * @Date: 2019-04-18 20:42
 * @Description:
 */
@Component
public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String name = request.getParameter("name");
        if(name == null || name.equals("")){
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            return null;
        }
        System.out.println("name: " + name);
        return null;
    }
}

重启 zuulService,再次访问 http://localhost:8766/client1/hello ,由于没有 name 参数,会报在过滤器中设置的 401 错误

关于过滤器中几个方法:

filterType():return:返回过滤器的类型

        pre:请求被路由之前调用

        routing:请求被路由的时候被调用

        post:routing 或 error 结束之后调用

        error:路由之后且发生错误时调用

filterOrder():return:过滤器的执行顺序

shouldFilter():return:是否要执行该过滤器,可以根据内部逻辑检测是否需要执行过滤器

run():return:过滤器的具体逻辑

猜你喜欢

转载自blog.csdn.net/Wyx_wx/article/details/89378937