欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringCloud- 第十二篇 Zuul概念及原理(一)

程序员文章站 2022-06-16 22:19:53
...

1:Zuul是什么

Zuul是Netflix开源的API网关。
API网关,类似于面向对象设计模式中的Facade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤,它除了要实现请求路由,负载均衡,来源合法性检测,权限校验,反爬虫等功能之外,还需要更多能力,比如与服务治理框架的结合,请求转发时的熔断机制,服务的聚合等一系列的高级功能。
官网:https://github.com/Netflix/zuul/

2:Zuul能干什么

  • 身份验证和安全 - 识别每个资源的身份验证要求,并拒绝不满足的请求
  • 审查和监测 - 跟踪边缘的有意义的数据和统计数据,以便我们准确地了解生产运行情况
  • 动态路由 - 根据需要将请求动态路由到不同的后端集群
  • 压力测试 - 逐渐增加到集群的流量,以衡量性能
  • 负载分配 - 为每种类型的请求分配容量并删除超出限制的请求
  • 静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群

3: 流程图

SpringCloud- 第十二篇 Zuul概念及原理(一)
            
    
    博客分类: javaspringcloud  
image.png

4:Zuul有什么

Zuul包含了对请求的路由和过滤两个最主要的功能:

其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础
而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础
然而实际上,路由功能在真正运行时,它的路由映射和请求转发都是由几个不同的过滤器完成的。其中,路由映射主要通过pre类型的过滤器完成,它将请求路径与配置的路由规则进行匹配,以找到需要转发的目标地址;而请求转发的部分则是由route类型的过滤器来完成,对pre类型过滤器获得的路由地址进行转发

5:HelloWorder

  • 加入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
  • 配置示例,注意serviceId需要全小写
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
server.port=8780
spring.application.name=service-zuul

zuul.routes.api-a.path=/aa/**
zuul.routes.api-a.serviceId=userservice

zuul.routes.api-b.path=/bb/**
zuul.routes.api-b.serviceId=service-feign
  • 启动类加入注解
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication

1:@EnableZuulProxy开启Zuul服务,将本地调用转发到相应的服务,Zuul使用Ribbon来定位一个要转发到的服务实例,并且所有请求都以 hystrix命令执行
2:注意@EnableZuulProxy不包括发现服务的客户端,因此对于基于服务ID的路由,还需要提供服务治理(例如Eureka)
3:使用@EnableZuulServer也可以运行不带代理的Zuul服务器,差别是它不会自动添加任何代理过滤器,也没有服务发现和代理

  • 测试运行
    1:如果配置文件里面没有配置zuul的话,zuul会把Eureka Server里面的服务都读取出来,此时运行的时候,需要用各个服务的serviceId做为ContextPath
    2:如果配置了zuul,还可以用配置的前缀代替服务的serviceId,然后进行访问
    3:如果不希望直接使用serviceId进行访问,可以配置ignored-services: '’,例如:zuul.ignored-services='’,当然可以不用*,而是直接把要禁用的服务的serviceId罗列出来,用逗号分开

6:传统路由配置

所谓的传统路由配置方式就是在不依赖于服务发现机制的情况下,通过在配置文件中具*定每个路由表达式实例的映射关系来实现api网关对外部请求的路由。

  • 单实例配置:
    通过zuul.routes.<route>.path与zuul.routes.<route>.url参数对的方式进行配置,如:
zuul.routes.cc.path=/cc/**
zuul.routes.cc.url=http://localhost:8769
  • 多实例配置
    通过zuul.routes.<route>.path与zuul.routes.<route>.serviceId参数的方式进行配置,结合Ribbon来做负载均衡,且让Ribbon关闭和Eureka的结合,比如:
zuul.routes.cc.path=/cc/**
#zuul.routes.cc.url=http://localhost:8769
zuul.routes.cc.serviceId=ccservice

ribbon.eureka.enabled=false
ccservice.ribbon.listOfServers=http://localhost:8769/,http://localhost:8768/   

这种简单的URL路由不会被执行为HystrixCommand,也不能使用Ribbon对多个URL进行负载平衡

7:服务路由配置

spring cloud zuul整合了spring cloud eureka,实现了对服务实例的自动化维护,那么使用服务路由配置的时候,不需要像传统路由配置方式那样为serviceId指定具体服务实例地址,只需要通过zuul.routes.<route>.path与zuul.routes.<route>.serviceId参数对的方式进行配置即可。
还可以简化配置,直接设置<route>为具体的serviceId,例如:

zuul.routes.service-feign.path=/dd/**

请记得开启前面示例中关闭的Ribbon和Eureka的结合。

8:服务路由的默认规则

大部分的路由规则机会都会采用服务名作为外部请求的前缀,zuul已经自动的帮我们实现以服务名作为前缀的映射,不需要去配置它。
如果不希望外部能直接访问,可以使用zuul.ignored-services参数来设置一个服务名匹配表达式来定义不自动创建路由的规则。

9:自定义路由映射关系

有时候,为了兼容外部不同版本的客户端程序,我们需要在微服务接口上定义版本的标记,比如:service-v1、 service-v2,而这样在映射的时候不太好管理。
通常的做法是为这些不同版本的微服务应用,生成以版本号作为路由前缀定义规则的路由规则,比如/v1/userservice/,具体示例如下:

@Bean
public PatternServiceRouteMapper serviceRouteMapper(){
    return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)","${version}/${name}");
}

路径匹配

在zuul中,路由匹配的路径表达式采用ant风格定义:
1:? 匹配任意单个字符
2:* 匹配任意数量的字符
3:** 匹配任意数量的字符,支持多级目录

注意:当存在多个匹配的路由规则时,匹配结果完全取决于路由规则的定义顺序
由于properties的配置内容无法保证有序,所以当出现这样的情况的时候,为了保证路由的优先顺序,需要使用yml文件来配置,以实现有序的路由规则

10:cookie与头信息

默认情况下,spring cloud zuul在请求路由时,会过滤掉http请求头信息中一些敏感信息,防止它们被传递到下游的外部服务器。
默认的敏感头信息通过zuul.sensitiveHeaders参数定义,默认包括cookie,set-Cookie,authorization三个属性。
这会引发一个常见的问题,使用了spring security,shiro等安全框架构建的web应用,通过spring cloud zuul构建的网关来进行路由时,由于cookie信息无法传递,web应用将无法实现登录和鉴权。为了解决这个问题,配置的方法可以为:

1:通过设置全局参数为空来覆盖默认值,如:
zuul.sensitiveHeaders=
不推荐这种做法,范围太大
2:将指定路由的敏感头设置为空,如:
zuul.routes.userservice.sensitiveHeaders=

11:重定向问题

使用API网关的一个重要原因就是将网关作为统一入口,从而不暴露所有内部服务细节。但我们在应用内部跳转的url却是具体web应用实例的地址,而不是通过网关的路由地址,该问题的根本原因在于spring cloud zuul在路由请求时,并没有将最初的host信息设置正确,怎么办呢?
配置zuul.add-host-header=true即可

12:路由回退

12.1:Hystrix和ribbon支持

  • Spring Cloud Zuul默认集成了Hystrix和Ribbon,自然就拥有线程隔离和断路器,以及对服务调用的客户端负载均衡功能。 需要注意的是,当使用path与url的映射关系来配置路由规则的时候,对于路由转发的请求不会采用hystrixCommand来包装,所以这类请求没有线程隔离和断路器的保护,并且也不会有负载均衡的能力。因此,我们在使用zuul的时候尽量使用path和serviceId的组合来进行配置,这样不仅可以保证api网关的健壮和稳定,也能用到ribbon的客户端负载均衡功能。
  • 所有路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHORE

12.2:Hystrix的路由回退

  • 可以通过创建ZuulFallbackProvider类型的bean来提供回退响应
  • zuul首先会去判断是否存在自定义的zuulFallbackProvider,如果有,那么直接回调你自定义实现类的fallbackResponse()方法。如果不存在会走hystrix的fallback逻辑(有可能直接抛出异常)
  • 虽然Zuul提供了降级的回调方法fallbackResponse(),但是这个方法是无参的,也就是说此时虽然你能够给调用端返回一个消息,但是此时你并不知道发生了什么样的异常(也就是说在这里你是获取不到异常信息的)。

13:请求路由其它配置

  • 路由前缀
    zuul.prefix:为路由规则增加前缀,例如: zuul.prefix=/user。
    zuul.strip-prefix(默认为true):这个设置是在转发请求之前,从请求中删除代理前缀
  • 本地跳转
    通过在serviceId中使用forward来指定需要跳转的服务器资源路径,例如:
    zuul.routes.api-t.path=/tt/**
    zuul.routes.api-t.serviceId=forward:/tt
  • 忽略表达式
    zuul.ignored-patterns:设置不希望被api网关进行路由的url表达式。例如:
    ignoredPatterns: //products/
    注意:它的范围并不是针对某个路由,而是对所有路由,所以要小心使用。
  • 路由端点
    使用@EnableZuulProxy,将启用/routes端点,可返回映射路由的列表。
    可以通过将endpoints.routes.enabled设置为false来禁用此端点
  • 配置属性
    zuul.max.host.connections已被两个新属性zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections替换,分别默认为200和20
  • 性能优化
    Zuul内部路由可以理解为使用一个线程池去发送路由请求,所以我们也需要扩大这个线程池的容量,如:
    zuul.host.max-per-route-connections=1000
    zuul.host.max-total-connections=1000