Spring Cloud 之 Zuul.

一、概述

?API 网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的 Facade 模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由、负载均衡、校验过滤等功能之外,还需要更多能力,比如与服务治理框架的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。

?在 Spring Cloud 中了提供了基于 Net?ix Zuul 实现的 API 网关组件 Spring Cloud Zuul。

二、准备阶段

SpringBoot 版本号:2.1.6.RELEASE
SpringCloud 版本号:Greenwich.RELEASE

pom.xml

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

application.yml

server:
  port: 5555

spring:
  application:
    name: cloud-zuul

eureka:
  client:
    service-url:
      defaultZone: http://user:[email protected]:1111/eureka/

ZuulApplication.java

// 开启 Zuul 的Api 网关服务功能
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulApplication {

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

三、请求转发

Spring Cloud Zuul 通过与 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得了所有其他微服务的实例信息。这样的设计非常巧妙地将服务治理体系中维护的实例信息利用起来,使得将维护服务实例的工作交给了服务治理框架自动完成,不再需要人工介入。而对于路由规则的维护, Zuul 默认会将通过以服务名作为
ContextPath 的方式来创建路由映射。比如上面的配置,Spring Cloud Zuul 会为 Eureka 中的每个服务都自动创建一个默认路由规则,默认规则的 path 会使用 serviceId 配置的服务名作为请求前缀 —— 对于 /‘serviceId‘/** 的请求,会被转发到 serviceId 的服务处理。

可以设置不对每个服务自动创建路由规则吗?

zuul:
  # Zuul 将对所有的服务都不自动创建路由规则
  ignored-services: "*"

如果我们手动配置路由是怎样的呢?推荐下面的方式:

zuul:
  routes:
    client-2:
      path: /client-2/**
      serviceId: cloud-eureka-client
    # zuul.routes.<serviceid> = <path>
    cloud-eureka-client: /client-3/**
    client-4:
      path: /client-4/**
      # 请求转发 —— 仅限转发到本地接口
      url: forward:/local

其中, ?:匹配任意单个数量字符;*:匹配任意多个数量字符;**:匹配任意多个数量字符,支持多级目录。

不推荐使用 url 的方式来配置路由,该请求是直接通过 httpClient 包实现的, 而没有使用 Hystrix 命令进行包装, 所以这类请求并没有线程隔离和断路器的保护。

如果我们要过滤掉某些 url,让它不走路由规则呢?

zuul:
  # 对某些 url 设置不经过路由选择
  ignored-patterns: {"/**/world/**","/**/zuul/**"}

Spring Cloud Zuul 对 "/zuul" 的路径访问的会绕过 dispatcherServlet, 被 ZuulServlet 处理,主要用来应对处理大文件上传的情况。

zuul:
  servlet-path: /zuul

四、请求过滤

Spring Cloud Zuul 提供了一套过滤器机制,开发者可以通过使用 Zuul 来创建各种校验过滤器,然后指定哪些规则的请求需要执行校验逻辑,只有通过校验的才会被路由到具体的微服务接口,不然就返回错误提示。

要在 Zuul 实现过滤器机制也很简单,只需要继承 ZuulFilter 类即可。接下来,我们来写一个过滤器 TokenFilter,校验接口参数中是否有 token 参数。

@Component
public class TokenFilter extends ZuulFilter {

    private Logger logger = LoggerFactory.getLogger(TokenFilter.class);

    /**
     * 过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为 pre, 代表会在请求被路由之前执行。路由类型有下面几种:
     * <p>
     * - pre: 可以在请求被路由之前调用。
     * - routing: 在路由请求时被调用。
     * - post: 在 routing 和 error 过滤器之后被调用。
     * - error: 处理请求时发生错误时被调用。
     *
     * @return
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行,数值越小,优先级越高。
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 判断该过滤器是否需要被执行。这里我们直接返回了true, 因此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的有效范围。
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器的具体执行逻辑
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        logger.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            logger.warn("token is empty");
            // 令 zuul 过滤该请求,不对其进行路由
            ctx.setSendZuulResponse(false);
            // 设置返回的错误码
            ctx.setResponseStatusCode(401);
            // 设置返回的 body
            ctx.setResponseBody("");
            return null;
        }
        logger.info("access token is ok");
        return null;
    }
}

实际上,上面提到的 Zuul 路由功能在真正运行时,它的路由映射和请求转发都是由几个不同的过滤器完成的。所以,过滤器可以说是 Zuul 实现 API 网关功能最为核心的部件,每一个进入 Zuul 的 HTTP 请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。下图源自 Zuul 的官方Wiki 中关于请求生命周期的图解, 它描述了一个 HTTP 请求到达 API 网关之后, 如何在各种不同类型的过滤器之间转的详细过程。

当外部 HTTP 请求到达 API 网关服务的时候,首先它会进入第一个阶段 pre, 在这里它会被 pre 类型的过滤器进行处理, 该类型过滤器的主要目的是在进行请求路由之前做一些前置加工,比如请求的校验、限流等。在完成了 pre 类型的过滤器处理之后,请求进入第二个阶段 routing, 也就是之前说的路由请求转发阶段,请求将会被 routing 类型过滤器处理。这里的具体处理内容就是将外部请求转发到具体服务实例上去的过程,当服务实例将请求结果都返回之后,routing 阶段完成, 请求进入第三个阶段 post。此时请求将会被 post 类型的过滤器处理,这些过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息,所以在 post 类型的过滤器中,我们可以对处理结果进行一些加工或转换等内容。另外,还有一个特殊的阶段 error, 该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是 post 类型的过滤器,因为它需要通过 post 过滤器将最终结果返回给请求客户端。

Zuul 中默认实现的 Filter:

类型 顺序 过滤器 功能
pre -3 ServletDetectionFilter 标记处理 Servlet 的类型
pre -2 Servlet30WrapperFilter 包装 HttpServletRequest 请求
pre -1 FormBodyWrapperFilter 包装请求体
route 1 DebugFilter 标记调试标志
route 5 PreDecorationFilter 处理请求上下文供后续使用
route 10 RibbonRoutingFilter serviceId
route 100 SimpleHostRoutingFilter url 请求转发
route 500 SendForwardFilter forward 请求转发
post 0 SendErrorFilter 处理有错误的请求响应
post 1000 SendResponseFilter 处理正常的请求响应

我们可以在配置文件中,选择是否禁用某个过滤器。

zuul:
  # 禁用某个过滤器 zuul.<SimpleClassName>.<filterTye>.disable=true
  TokenFilter:
    pre:
      disable: true

常常 request 中有些 header 信息我们不希望渗透到服务中去,比如 accessToken、sign、Cookie 等。或者我们要保持 request 的 host 信息一致,该怎么配置呢?

zuul:
  routes:
    client-2:
      path: /client-2/**
      serviceId: cloud-eureka-client
      # 敏感头信息设置为空,表示不过滤敏感头信息,允许敏感头信息渗透到下游服务器(针对单个服务的敏感头部信息配置,下面两个配置项选其一即可)
      sensitiveHeaders: ""
      customSensitiveHeaders: true

  # Spring Cloud Zuul在请求路由时,会过滤掉 HTTP 请求头(Cookie、Set-Cookie、Authorization)信息中的一些敏感信息,
  sensitive-headers: {"Cookie", "Set-Cookie", "Authorization"}
  # 网关在进行路由转发时为请求设置 Host 头信息(保持在路由转发过程中 host 头信息不变)
  add-host-header: true
  # 请求转发时加上 X-Forwarded-*头域
  add-proxy-headers: true

五、Hystrix 和 Ribbon 支持

# 该参数可以用来设置 API 网关中路由转发请求的 HystrixCommand 执行超时时间,单位为毫秒。
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutinMilliseconds: 5000

ribbon:
  # 该参数用来设置路由转发请求的时候,创建请求连接的超时时间。
  ConnectTimeout: 500
  # 该参数用来设置路由转发请求的超时时间。
  ReadTimeout: 2000
  # 最大自动重试次数
  MaxAutoRetries: 1
  # 最大自动重试下一个服务的次数
  MaxAutoRetriesNextServer: 1

其中,Hystrix 的配置参数可以在 HystrixCommandProperties.java 中找到。

其中,Ribbon 的配置参数可以在 CommonClientConfigKey.java 中找到。

另外需要注意的是,请求重试还需要将 zuul.retryable 设置为 true。

原文地址:https://www.cnblogs.com/jmcui/p/11219820.html

时间: 2024-10-03 13:43:37

Spring Cloud 之 Zuul.的相关文章

Spring Cloud系列-Zuul网关集成JWT身份验证

前言 这两三年项目中一直在使用比较流行的spring cloud框架,也算有一定积累,打算有时间就整理一些干货与大家分享. 本次分享zuul网关集成jwt身份验证 业务背景 项目开发少不了身份认证,jwt作为当下比较流行的身份认证方式之一主要的特点是无状态,把信息放在客户端,服务器端不需要保存session,适合分布式系统使用. 把jwt集成在网关的好处是业务工程不需要关心身份验证,专注业务逻辑(网关可验证token后,把解析出来的身份信息如userId,放在请求头传递给业务工程). 顺便分享下

(四)Spring Cloud教程——Zuul(F版本)

参考:方志鹏的专栏 在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现.服务消费.负载均衡.断路器.智能路由.配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统.一个简单的微服务系统如下图: 注意:A服务和B服务是可以相互调用的,作图的时候忘记了.并且配置服务也是注册到服务注册中心的. 在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul.Ngnix),再到达服务网关(zuul集群),然后再到具体的服.,服务统一注

Spring Cloud之Zuul负载均衡

Zuul网关默认是实现负载均衡的,不需要任何配置.默认开启ribbon效果的 可以启启动两个服务端口,访问下. 原文地址:https://www.cnblogs.com/toov5/p/9972466.html

spring cloud 学习笔记--网关zuul学习

微服务是多个服务共同完成一件事情,那么"一致对外"就很有必要,就像我们去买面包,不可能先去找农民买小麦,再.... 盗图 spring cloud 引入zuul方式来实现这一功能 添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </depend

Spring Cloud(Dalston.SR5)--Zuul 网关-路由配置

Spring Cloud 在 Zuul 的 routing 阶段实现了几个过滤器,这些过滤器决定如何进行路由工作. 简单路由(SimpleHostRoutingFilter) 该过滤器运行后,会将 HTTP 请求全部转发到"源服务器",简单路由的配置如下: #zuul路由配置 zuul: routes: #表示http://localhost:9100/person/speaks地址,路由到http://localhost:8080/speaks person: path:/perso

API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd(转)

前几天拜读了 OpsGenie 公司(一家致力于 Dev & Ops 的公司)的资深工程师 Turgay ?elik 博士写的一篇文章(链接在文末),文中介绍了他们最初也是采用 Nginx 作为单体应用的网关,后来接触到微服务架构后开始逐渐采用了其他组件. 我对于所做的工作或者感兴趣的技术,喜欢刨根问底,所以当读一篇文章时发现没有看到我想要看到的设计思想,我就会四处搜集资料,此外这篇文章涉及了我正在捣鼓的 Spring Cloud,所以我就决定写一篇文章,争取能从设计思路上解释为什么会有这样的性

Spring Boot + Spring Cloud 构建微服务系统(七):API服务网关(Zuul)

技术背景 前面我们通过Ribbon或Feign实现了微服务之间的调用和负载均衡,那我们的各种微服务又要如何提供给外部应用调用呢. 当然,因为是REST API接口,外部客户端直接调用各个微服务是没有问题的,但出于种种原因,这并不是一个好的选择. 让客户端直接与各个微服务通讯,会有以下几个问题: 客户端会多次请求不同的微服务,增加了客户端的复杂性. 存在跨域请求,在一定场景下处理会变得相对比较复杂. 实现认证复杂,每个微服务都需要独立认证. 难以重构,项目迭代可能导致微服务重新划分.如果客户端直接

Zuul &amp; Spring Cloud Gateway &amp; Linkerd性能对比

Spring Cloud从一开始最受大家质疑的就是网关性能,那是由于Spring Cloud最初选择了使用Netflix几年前开源的Zuul作为基础,而高性能版的Zuul 2在经过了多次跳票之后,对于Spring这样的整合专家可能也不愿意再继续等待,Spring Cloud Gateway应运而生,那么这个由Spring社区自己开发推出的网关又如何呢?最近一篇<实测 | 转型微服务,这4大工具谁是API网关性能最优?>的翻译中对比了NGINX.ZUUL.Spring Cloud Gateway

Spring Cloud Netflix子模块综合整理-Zuul

路由器和过滤器:Zuul 路由是微服务架构的组成部分. 例如,/ 可以映射到您的Web应用程序,/api /users映射到用户服务,/api/ shop映射到购物服务. Zuul是Netflix基于JVM的路由器和服务器端负载均衡器. Netflix使用Zuul进行以下操作: ? 配置属性zuul.max.host.connections已被两个新属性替换,zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections,分别默认为2