1Zuul是什么
Zuul是Netflix开源的API网关。
API网关类似于面向对象设计模式中的Facade模式它的存在就像是整个微服务架构系统的门面一样所有的外部客户端访问都需要经过它来进行调度和过滤它除了要实现请求路由负载均衡来源合法性检测权限校验反爬虫等功能之外还需要更多能力比如与服务治理框架的结合请求转发时的熔断机制服务的聚合等一系列的高级功能。
官网https://github.com/Netflix/zuul/
2Zuul能干什么
- 身份验证和安全 - 识别每个资源的身份验证要求并拒绝不满足的请求
- 审查和监测 - 跟踪边缘的有意义的数据和统计数据以便我们准确地了解生产运行情况
- 动态路由 - 根据需要将请求动态路由到不同的后端集群
- 压力测试 - 逐渐增加到集群的流量以衡量性能
- 负载分配 - 为每种类型的请求分配容量并删除超出限制的请求
- 静态响应处理 - 直接在边缘构建一些响应而不是将它们转发到内部集群
3: 流程图
4Zuul有什么
Zuul包含了对请求的路由和过滤两个最主要的功能
其中路由功能负责将外部请求转发到具体的微服务实例上是实现外部访问统一入口的基础
而过滤器功能则负责对请求的处理过程进行干预是实现请求校验、服务聚合等功能的基础
然而实际上路由功能在真正运行时它的路由映射和请求转发都是由几个不同的过滤器完成的。其中路由映射主要通过pre类型的过滤器完成它将请求路径与配置的路由规则进行匹配以找到需要转发的目标地址而请求转发的部分则是由route类型的过滤器来完成对pre类型过滤器获得的路由地址进行转发
5HelloWorder
- 加入依赖
<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..path与zuul.routes..url参数对的方式进行配置如
zuul.routes.cc.path=/cc/**
zuul.routes.cc.url=http://localhost:8769
- 多实例配置
通过zuul.routes..path与zuul.routes..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..path与zuul.routes..serviceId参数对的方式进行配置即可。
还可以简化配置直接设置为具体的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文件来配置以实现有序的路由规则
10cookie与头信息
默认情况下spring cloud zuul在请求路由时会过滤掉http请求头信息中一些敏感信息防止它们被传递到下游的外部服务器。
默认的敏感头信息通过zuul.sensitiveHeaders参数定义默认包括cookie,set-Cookie,authorization三个属性。
这会引发一个常见的问题使用了spring securityshiro等安全框架构建的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.1Hystrix和ribbon支持
- Spring Cloud Zuul默认集成了Hystrix和Ribbon自然就拥有线程隔离和断路器以及对服务调用的客户端负载均衡功能。 需要注意的是当使用path与url的映射关系来配置路由规则的时候对于路由转发的请求不会采用hystrixCommand来包装所以这类请求没有线程隔离和断路器的保护并且也不会有负载均衡的能力。因此我们在使用zuul的时候尽量使用path和serviceId的组合来进行配置这样不仅可以保证api网关的健壮和稳定也能用到ribbon的客户端负载均衡功能。
- 所有路由的默认Hystrix隔离模式ExecutionIsolationStrategy为SEMAPHORE
12.2Hystrix的路由回退
- 可以通过创建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