博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Spring Cloud] 4.8 Router and Filter: Zuul
阅读量:6101 次
发布时间:2019-06-20

本文共 10180 字,大约阅读时间需要 33 分钟。

  hot3.png

4.8 Router and Filter: Zuul

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

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

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

提供一下功能:

  • 身份验证
  • 推断
  • 压力测试
  • 金丝雀测试(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: usersribbon:  eureka:    enabled: falseusers:  ribbon:    listOfServers: example.com,google.com

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

ApplicationConfiguration.java

@Beanpublic PatternServiceRouteMapper serviceRouteMapper() {    return new PatternServiceRouteMapper(        "(?
^.+)-(?
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: 60000ribbon:  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服务吗? 开发了一套: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: 5678spring:  application:    name: sidecarsidecar:  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: passwordinfo:  description: Spring Cloud Samples  url: https://github.com/spring-cloud-samples

转载于:https://my.oschina.net/roccn/blog/826655

你可能感兴趣的文章
从0开始学Flutter
查看>>
mysql操作入门基础之对数据库和表的增删改查
查看>>
IIS负载均衡
查看>>
分布式事务,EventBus 解决方案:CAP【中文文档】
查看>>
Linux下的CPU性能瓶颈分析案例
查看>>
spring mvc入门
查看>>
2012在数据库技术会议上的讲话PPT打包
查看>>
【Android】 TextView设置个别字体样式
查看>>
python svn
查看>>
raise语句
查看>>
sequence2(高精度dp)
查看>>
如何向 Linux 内核上游提交 Patch ?
查看>>
Go编程笔记(7)
查看>>
Go语言int类型绑定方法
查看>>
pid控制的文章
查看>>
MySQL中EXPLAIN命令详解
查看>>
redis 单点部署
查看>>
JEPLUS报表制作——JEPLUS软件快速开发平台
查看>>
初始化Windows powershell,连接Python
查看>>
Hardware Scaler for Performance and Efficiency
查看>>