文档章节

[Spring Cloud] 4.8 Router and Filter: Zuul

秋雨霏霏
 秋雨霏霏
发布于 2017/01/17 17:43
字数 3523
阅读 1707
收藏 7

4.8 Router and Filter: Zuul

=== 4.8 路由及过滤器:Zuul

路由功能是微服务框架中一个不可或缺的部分。例如:/可以映射到一个web应用中,/api/users会被映射到用户服务;/api/shop会被映射到商品服务……

Zuul是Netflix的一个基于JVM路由以及服务端负载均衡的框架。

Netflix通过Zuul提供一下功能:

  • 身份验证
  • 推断
  • 压力测试
  • 金丝雀测试(canary testing)
  • 动态路由
  • 服务迁移
  • 反压减负
  • 安全
  • 静态资源处理
  • 主主通讯管理

Zuul的规则引擎支持任何基于JVM的语言来编写规则以及过滤器,例如:Java,Groovy。

注意: 原有的zuul.max.host.connections属性,已经被zuul.host.maxTotalConnectionszuul.host.maxPerRouteConnections两个新的配置项替代。默认值分别为:200,20。

注意: 默认的Hystrix隔离模式(ExecutionIsolationStrategy)对于所有的路由都是SEMAPHORE。可以通过配置zuul.ribbonIsolationStrategy来自定义,如:THREAD

4.8.1 How to Include Zuul 如何引入Zuul

一如既往,在工程中引入相应的启动器就行:group:org.springframework.cloud;artifact id:spring-cloud-starter-zuul

4.8.2 Embedded Zuul Reverse Proxy 内嵌Zuul反向代理

Spring Cloud 会创建一个内嵌的Zuul代理,来简化前端UI应用调用后端服务的开发。对于前端接口调用这个功能会比较有用,避免手工处理复杂的跨域(CORS)以及交叉身份验证等后端一系列问题。

如果需要开启此功能,只需要在Spring Boot 的主类中加上@EnableZuulProxy注解就行。按约定,假设一个ID为“users”的服务,会收到代理服务的/users的请求。代理服务会使用Ribbon的负载来转发请求,所有的请求也将由一个Hystrix命令来执行,这样也就自带降级,断路器等功能。

注意: Zuul的启动器没有包括服务发现的客户端,因此,需要手工引入服务发现框架(例如:Eureka)

如果需要不想自动添加服务,可以设置zuul.ignored-services来指定一组服务id来匹配。如果不希望通过匹配来发现服务,也可以直接进行路由映射配置,例如:

application.yml

zuul:
  ignoredServices: '*'
  routes:
    users: /myusers/**

上例中,除了“users”服务,其他的服务都会被忽略。

可以添加额外的路由配置,例如:

application.yml

zuul:
  routes:
    users: /myusers/**

这个示例,配置了前端通过/myusers的http访问,将会被后端“users”服务处理(例如:/myusers/101将会转发的/101

如果需要更精细的控制路由,如指定路径、服务id,可以这样:

application.yml

zuul:
  routes:
    users:
      path: /myusers/**
      serviceId: users_service

这个示例,配置了前端/myusers请求,会被转发到后端“users_service”服务。path可以指定一个正则表达式来匹配路径,因此,/myusers/*只能匹配一级路径,但是通过/myusers/**可以匹配所有以/myusers/开头的路径。

后端服务除了可以通过serviceId(基于服务发现)来指定外,还可以通过url(物理路径/直连)来指定。例如:

application.yml

zuul:
  routes:
    users:
      path: /myusers/**
      url: http://example.com/users_service

这个简单的url路由不会由HystrixCommand来执行,自然,也就得不到Ribbon的负载均衡,降级,断路器等功能。如果需要这些,可以指定一个服务路由并且为这个serviceId配置一个Ribbon客户端。例如:

application.yml

zuul:
  routes:
    users:
      path: /myusers/**
      serviceId: users

ribbon:
  eureka:
    enabled: false

users:
  ribbon:
    listOfServers: example.com,google.com

可以一个转换器,让serviceId和路由之间使用正则表达式来自动匹配。例如:

ApplicationConfiguration.java

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

这样,一个serviceId为“myusers-v1”的服务,就会被映射到路由为“/v1/myusers/”的路径上。任何正则表达式都可以,但是所有的命名组必须包括servicePatternroutePattern两部分。如果servicePattern没有匹配一个serviceId,那就会使用默认的。在上例中,一个serviceId为“myusers”的服务,将会被映射到路由“/myusers/”中(不带版本信息)。这个特性默认是关闭的,而且只适用于已经发现的服务。

可以通过zuul.prefix为所有的映射增加统一的前缀。如:/api。默认情况下,代理会在转发前自动剥离这个前缀。如果需要转发时带上前缀,可以配置:zuul.stripPrefix=false来关闭这个默认行为。例如:

application.yml

zuul:
  routes:
    users:
      path: /myusers/**
      stripPrefix: false

注意: zuul.stripPrefix只会对zuul.prefix的前缀起作用。对于path指定的前缀不会起作用。

上例中,请求/myusers/101,将会被转发到/myusers/101的“users”服务。

zuul.routes实体实际上会绑定一个ZuulProperties对象。如果你看看这个对象,你会发现它也有一个“retryable”属性。如果设置为“true”,那Ribbon客户端会自动失败重试。(如果需要,可以修改这个属性)

默认情况下,转发的请求,头信息会添加X-Forwarded-Host信息。可以通过配置:zuul.addProxyHeaders = false来关闭这个特性。前缀默认会被剥离,但是后端可以通过X-Forwarded-Prefix拿到前缀。

如果配置了一个默认路由:/,那么当应用带有@EnableZuulProxy注解时,可以得到一个独立的服务。例如:zuul.route.home: /,那所有的请求(“/**”)都会转发到“home”服务。

如果还需要更进一步的控制忽略规则,可以配置忽略的路径匹配规则。要注意的是匹配是从前缀开始的,并且会匹配所有符合条件的服务。例如:

application.yml

zuul:
  ignoredPatterns: /**/admin/**
  routes:
    users: /myusers/**

这个示例中,“/myusers/101”将会转发到“/101”到“users”服务。但是如果包含“/admin”则不会转发。

警告: 如果想按找配置的顺序进行路由规则控制,则需要使用YAML,如果是使用propeties文件,则会丢失顺序。例如:

application.yml

zuul:
  routes:
    users:
      path: /myusers/**
    legacy:
      path: /**

如果使用properties文件进行配置,则legacy就可能会先生效,这样users就没效果了。

4.8.3 Zuul Http Client

Zuul默认会使用Apache HTTP客户端,而不是使用Ribbon的RestClient。如果想要使用RestClient或者okhttp3.OkHttpClient,可以配置:ribbon.restclient.enabled=true 或则 ribbon.okhttp.enabled=true

4.8.3.1 Cookies and Sensitive Headers

同一个系统中各个服务之间通过Headers来共享信息是没啥问题的,但是如果不想Headers中的一些敏感信息随着HTTP转发泄露出去话,需要在路由配置中指定一个忽略Header的清单。

如同在浏览器中一样,Cookies也是一个敏感信息源,所以也需要一个特殊的规则来进行配置。 如果消费端是一个浏览器,那么Cookies也会对下游服务造成一定的影响(在下游服务看来这些cookies信息都来自同一个地方)

如果精心设计了个服务,只有一个下游服务会设置cookies,那么这些信息会随着后端返回给所有的调用者。同样,如果由代理来设置cookies,那后端服务自然的就会共享这些信息。

另外,下游服务对cookies的读写,可能并不会对调用者有啥实际作用,因此建议大家,在路由中至少设置"Set-Cookie"、"Cookie"这两个Header。即便路由到本地服务,这样意味着允许代理,服务之间传递指定cookies信息。

配置的sensitiveHeaders可以用逗号分割。例如:

application.yml

zuul:
  routes:
    users:
      path: /myusers/**
      sensitiveHeaders: Cookie,Set-Cookie,Authorization
      url: https://downstream

也可以为sensitiveHeaders指定一个全局的配置:zuul.sensitiveHeaders。这样,当路由中没有配置sensitiveHeaders那么就会采用全局配置。

注意: 全局配置是Spring Cloud Netflix 1.1中的新特性。

4.8.3.2 Ignored Headers

如果每一个路由配置额外的敏感Header,那你可以设置一个全局的zuul.ignoredHeaders来统一的组织下游服务随意的共享Header信息。

当然默认是没有这个配置的,如果引入Spring Security,那么会自动加上“security”Header信息。

下游服务也可以添加他们自己的Header。这种情况就需要保留这些Header信息,所以需要设置zuul.ignoreSecurityHeadersfalse。特别是在引入Spring Security时。

4.8.4 The Routes Endpoint 路由接口

如果在Spring Boot中使用了@EnableZuulProxy,那么默认就会开启一个额外的HTTP接口:/routes。GET请求这个接口会返回路由映射的清单。POST请求可以强制刷新某个存在的路由。

注意: 服务信息变更后,路由映射会自动更新。通过这个接口这是强制立即刷新。

4.8.5 Strangulation Patterns and Local Forwards 匹配范围及本地跳转

迁移系统,或者和旧系统整合时的常用套路就是逐步替代,灰色过度。在这一点上Zuul代理也比较方便,可以让客户端老接口全部走代理,通过路由转发到新的系统接口上。 例如:

application.yml

zuul:
  routes:
    first:
      path: /first/**
      url: http://first.example.com
    second:
      path: /second/**
      url: forward:/second
    third:
      path: /third/**
      url: forward:/3rd
    legacy:
      path: /**
      url: http://legacy.example.com

在上例中,legacy的匹配范围实际上被限制死了。注意:url中的forward的用法类似 Spring中的@RequestMapping

4.8.6 Uploading Files through Zuul 通过Zuul进行文件上传

如果已经使用@EnableZuulProxy,那么就已经可以通过代理进行文件上传操作了,当然文件不能过大。大文件最好直接通过Spring MVC的DispatcherServlet来处理/zuul/*请求。例如: 假设配置了zuul.routes.customers=/customers/**,那就可以POST请求/zuul/customers/*来处理大文件的上传操作。Servelt路径可以通过zuul.servletPath来指定。

如果文件真的比较大,那么还会导致上传过程中处理超时。所以,还需要配置一下超时设置。例如:

application.yml

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000

注意:大文件流的请求,需要使用chunked编码(一些浏览器默认就是chunked编码)。例如: 在命令行中可以这样:

$ curl -v -H "Transfer-Encoding: chunked" \
    -F "file=@mylarge.iso" localhost:9999/zuul/simple/file

4.8.7 Plain Embedded Zuul

如果使用@EnableZuulServer,可以抛开代理,直接使用Zuul服务,或者只让部分服务走代理。应用中,可以额外添加带有@EnableZuulProxyZuulFilter类型的Bean,这些Bean就会组成一个过滤器链。这样就是,通过过滤器来处理请求而不是走代理了。

这种情况下,Zuul服务的路由仍然可以通过zuul.routes.*进行配置,不过当然就不会有服务发现以及代理等功能了。因此,serviceIdurl就可以被忽略。例如:

application.yml

zuul:
  routes:
    api: /api/**

上例就是直接映射“/api/**”到Zuul的过滤器链。

4.8.8 Disable Zuul Filters 禁用Zuul过滤器

默认情况下,无论是代理模式还是服务模式,Spring Cloud都为Zuul配备了一组ZuulFilter。 具体开启了哪些过滤器,可以参见相关文档

如果想禁用这些过滤器,可以配置:zuul.<SimpleClassName>.<filterType>.disable=true。 按照惯例,这个过滤器都放置在包**.filters下。所以,如果想要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter过滤器,则可以配置:org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter

4.8.9 Providing Hystrix Fallbacks For Routes 为路由提供降级功能

可以创建一个Bean:ZuulFallbackProvider来为Zuul调用链路提供一个降级服务。在这个Bean中需要制定一个路由的ID,当触发降级时,由这个路由来返回ClientHttpResponse。下面展示了一个简单的ZuulFallbackProvider实例:

class MyFallbackProvider implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
        return "customers";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
        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".getBytes());
            }

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

相应的配置如下:

zuul:
  routes:
    customers: /customers/**

4.8.10 Polyglot support with Sidecar 跨语言支持

是否想在非JVM平台语言中使用Eureka、Ribbon及Config服务吗? Netflix Prana 开发了一套:Spring Cloud Netflix Sidecar。 它包含了一套简单的HTTP API(例如:主机名,端口),用于暴露各个服务端口。 你也可以通过Zuul内嵌的代理服务路由到Eureka服务。 Spring Cloud Config Server可以通过主机名直接访问,也可以通过Zuul代理访问。Sidecar中的非JVM应用可以实现一个健康检查机制,这样就可以在应用上下线时得到反馈。

在工程中引入Sidecar需要加入相关依赖:group:org.springframework.cloud,artifact id:spring-cloud-netflix-sidecar

开启Sidecar,只需要在Boot应用中加上@EnableSidecar注解。这个注解相当于:@EnableCircuitBreaker@EnableDiscoveryClient以及@EnableZuulProxy三个注解的合集。 这样,运行后就可以被非JVM应用访问。

可以在application.yml中,对Sidecar进行配置:sidecar.portsidecar.health-urisidecar.port指定一个端口,用于非JVM应用监听。这样Sidecar就可以将应用注册到Eureka。sidecar.health-uri指定一个URI,非JVM应用通过这个URI来模拟Spring Boot的健康检测。这个应该返回一个JSON格式。例如:

health-uri-document

{
  "status":"UP"
}

Sidecar应用的application.yml可以这样配置:

application.yml

server:
  port: 5678
spring:
  application:
    name: sidecar

sidecar:
  port: 8000
  health-uri: http://localhost:8000/health.json

/hosts/{serviceId}就相当于DiscoveryClient.getInstances()方法。 下面的例子,展示一个/hosts/customers返回两个不同主机上的实例。http://localhost:5678/hosts/{serviceId}这个接口也可以被非JVM应用访问到(假设Sidecar的监听端口为5678)。

/hosts/customers

[
    {
        "host": "myhost",
        "port": 9000,
        "uri": "http://myhost:9000",
        "serviceId": "CUSTOMERS",
        "secure": false
    },
    {
        "host": "myhost2",
        "port": 9000,
        "uri": "http://myhost2:9000",
        "serviceId": "CUSTOMERS",
        "secure": false
    }
]

Zuul代理会为Eureka服务,按照/<serviceId>,自动添加路由。相应的,消费端会增加到/customers。 非JVM应用可以访问这个接口来获取消费端信息:http://localhost:5678/customers(假设Sidecar的监听端口为5678)

如果Config Server已经注册到Eureka了,那非JVM应用也可以通过Zuul代理进行访问。如果ConfigServer的serviceId是configserver,Sidecar端口为5678,那就可以通过http://localhost:5678/configserver对Config Server进行访问。

非JVM应用可以通过Config Server获得YAML格式的配置信息。例如:调用http://sidecar.local.spring.io:5678/configserver/default-master.yml可以返回下列信息:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  password: password
info:
  description: Spring Cloud Samples
  url: https://github.com/spring-cloud-samples

© 著作权归作者所有

秋雨霏霏
粉丝 148
博文 94
码字总数 168411
作品 0
杭州
CTO(技术副总裁)
私信 提问
Spring Cloud Gateway初体验

版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接 ,博主地址:http://blog.csdn.net/forezp。 https://blog.csdn.net/forezp/article/details/83792388 转载请标明出处...

方志朋
2018/11/06
0
0
白话SpringCloud | 第九章:路由网关(Zuul)的使用

前言 介绍完分布式配置中心,结合前面的文章。我们已经有了一个微服务的框架了,可以对外提供api接口服务了。但现在试想一下,在微服务框架中,每个对外服务都是独立部署的,对外的api或者服...

oKong
2018/10/15
0
0
Spring cloud Netflix中的超时配置

一般SpringCloud中超时配置,含Hystrix/Ribbon两部分: hystrix thread timeout需要比ribbon timeout设置时间长。 ribbon有retry机制,如果timeout设置时间短,则无法retry。 在zuul中,如果...

遥借东风
2018/12/04
0
0
Spring Cloud学习:04路由网关(Zuul)

1 Zuul介绍 通过前几个核心组件,可以构建一个简略(不完善)的微服务架构: 在该架构中,我们的服务集群包含:内部服务Service A和Service B,他们都会注册与订阅服务至Eureka Server,而O...

寒武没有纪
2017/11/06
0
0
跟我学Spring Cloud(Finchley版)-18-Zuul深入

本节探讨Zuul的高级特性。 TIPS: 笔者已经写过很多Zuul相关的文章,对于已经写过的内容,就不再啰嗦一遍了,直接贴地址吧。 过滤器详解 过滤器是Zuul的核心,Zuul大多功能都是基于过滤器实现...

周立_ITMuch
01/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

NIO基于长度域的报文在Netty下的解码

1, 先复习一下粘包/拆包 1.1, 粘包/拆包的含义 TCP是个“流”协议, 并不了解上层业务数据的具体含义, 它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP...

老菜鸟0217
今天
8
0
从零开始搭建spring-cloud(2) ----ribbon

在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。 其实我们已经在上...

Vincent-Duan
今天
19
0
get和post的区别?

doGet:路径传参。效率高,安全性差(get的传送数据量有限制,不能大于2Kb) doPOST:实体传参。效率低,安全性好 建议: 1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Pos...

花无谢
昨天
4
0
当谈论迭代器时,我谈些什么?

当谈论迭代器时,我谈些什么? 花下猫语:之前说过,我对于编程语言跟其它学科的融合非常感兴趣,但我还说漏了一点,就是我对于 Python 跟其它编程语言的对比学习,也很感兴趣。所以,我一直...

豌豆花下猫
昨天
14
0
10天学Python直接做项目,我做了这5件事

初学者如何尽快上手python? 市面上关于如何学python的资料很多,但是讲的都太复杂。 我就是很简单的几句话,从小白到开发工程师,我只做了五件事。 我觉得任何商业计划书如果不能用几句话讲...

Python派森
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部