原文首发地址:Zuul,据说SpringCloud不许备要我了,但是为何面试还要每天问我?html
Spring Cloud 微服务精彩系列前端
- 阿里面试官问我:到底知不知道什么是Eureka,此次,我没沉默
- 万字详解Ribbon架构,针对面试高频题多角度细说Ribbon
- 什么是Hystrix,阿里技术最终面,遗憾的倒在Hystrix面前!
- 2万字好文全方位深刻学习SpringCloud Fegin,面试不在彷徨
- Zuul,据说SpringCloud不许备要我了,但是为何面试还要每天问我?
什么是Api网关?
咱们能够将API网关看作是一个反向路由器,它让咱们不须要关注内部的细节,API网关统一服务入口,可方便实现对平台众多服务接口进行管控。java
Api网关全部的调用者的请求都接收过来,而后经过指定的路由机制转发到特定的服务实例上。web
咱们能够将网关看作成一组过滤器组成的集合,有一些与业务无关的公共逻辑能够抽象到网关中实现,如安全认证、限流熔断、日志监控,黑白名单,缓存,压力测试、灰度发布等。面试
为何须要网关?
API网关给企业带来的好处
(1)网关层对外部和内部进行了隔离,保障了后台服务的安全性。spring
(2)对外访问控制由网络层面转换成了运维层面,减小变动的流程和错误成本。json
(3)减小客户端与服务的耦合,服务能够独立运行,并经过网关层来作映射。后端
(4)经过网关层聚合,减小外部访问的频次,提高访问效率。api
(5)节约后端服务开发成本,减小上线风险。浏览器
(6)为服务熔断,灰度发布,线上测试提供简单方案。
(7)便于进行应用层面的扩展。
上面展现的是API网关的核心架构图,在图中的核心功能模块中咱们能够看到API网关内部帮我实现了这么的多功能模块,看到API网关帮咱们实现了这么多的核心功能,我想你们应该都知道为何须要使用网关了吧。
什么是Zuul?
Zuul做为微服务系统的网关组件,是从设备和网站到Netflix流应用程序后端的全部请求的前门。zuul做为整个应用的流量入口,接收全部的请求,如app、网页等,而且将不一样的请求转发至不一样的处理微服务模块。做为边缘服务应用程序,Zuul旨在实现动态路由,监控,弹性和安全性。
Zuul 主要应用场景
1.黑白名单:实现经过IP地址控制禁止访问网关功能,此功能是应用层面控制实现,再往前也能够经过网络传输方面进行控制访问。
2.日志:实现访问日志的记录,可用于分析访问、处理性能指标,同时将分析结果支持其余模块功能应用。
3.协议适配:实现通讯协议校验、适配转换的功能。
4.身份认证:负责网关访问身份认证验证,此模块与“访问认证中心”通讯,实际认证业务逻辑交移“访问认证中心”处理。
5.计流限流:实现微服务访问流量计算,基于流量计算分析进行限流,能够定义多种限流规则。
6.路由:路由是API网关很核心的模块功能,此模块实现根据请求,锁定目标微服务并将请求进行转发。此模块须要与“服务发布管理中心”通讯。“服务发布管理中心”实现微服务发布注册管理功能,与其通讯得到目标微服务信息。
Zuul核心架构剖析
Zuul网关架构图
-
Zuul Servlet:zuul的servlet容器
-
Zuul Filter Runner:zuul执行filter的处理器
-
Pre routing Filter:zuul请求的前置过滤器
-
Routing Filter:zuul请求的路由放行过滤器
-
Post routing Filter:zuul请求的后置过滤器
-
Request Context:zuul servlet的上下文
-
Filter Loader:filter加载器
-
Filter File Manager:filter内容管理器
-
Filter Directory:filter过滤器存放路径
-
Filter Publisher:发布filter的处理类
-
Filter Persister:持久化filter的处理类
-
Filter Poller:轮询Persister中的filter并将新filter推送至Filter Directory
Zuul请求处理生命周期
Zuul请求的生命周期如图所示,该图详细描述了各类类型的过滤器的执行顺序。
-
http发送请求到zuul网关
-
zuul网关首先通过pre filter;
-
验证经过后进入routing filter,接着将请求转发给远程服务,远程服务执行完返回结果,若是出错,则执行error filter;
-
继续往下执行post filter;
-
最后返回响应给http 客户端。
Zuul的实战演练
第一步,新建一个模块ZuulServer
引入zuul和eureka client的依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
第二步,在Spring boot的主类上增长注解@EnableZuulProxy
package com.javaer.study.zuulserveer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/@SpringBootApplication@EnableZuulProxypublic class ZuulserveerApplication { public static void main(String[] args) { SpringApplication.run(ZuulserveerApplication.class, args); }}
第三步,添加application.yml配置
server: port: 8888spring: application: name: zuul-serverzuul: routes: #标识你服务的名字,这里能够本身定义,通常方便和规范来说仍是跟本身服务的名字同样 order-service: #服务映射的路径,经过这路径就能够从外部访问你的服务了,目的是为了避免爆露你机器的IP, #面向服务的路由了,给你选一个可用的出来, #这里zuul是自动依赖hystrix,ribbon的,不是面向单机 path: /order-service/** #这里必定要是你Eureka注册中心的服务的名称,是因此这里配置serviceId由于跟eureka结合了, #若是单独使用zuul,那么就必须写本身机器的IP了, #如url:http://localhost:8080/ 这样的很差就是写死IP了, #万一这IP挂了,这高可用性,服务注册那套东西就用不起来了 serviceId: order-serviceeureka: #客户端 client: #注册中心地址 service-url: defaultZone: http://localhost:8761/eureka/
代码部分完成。
接下来启动,首先启动Eureka server,而后启动以前文章中建立的OrderService,最后启动刚刚建立的ZuulServer。
而后在浏览器请求http://localhost:8888/order-service/test 便可。
Zuul路由详解
Zuul的路由包含两种路由
1. 传统路由
所谓的传统路由配置方式就是在不依赖于服务发现机制的状况下,经过在配置文件中具体指定每一个路由表达式与服务实例的映射关系来实现 API 网关对外不请求的路由。
2. 面向服务路由。
传统路由的配置方式须要运维人员花费大量的时间来维护各个路由 path 与 url 的关系。为了解决这个问题,Spring Cloud Zuul 实现了与 Spring Cloud Eureka 的无缝整合,咱们可让路由的 path 不是映射具体的 url ,而是让它映射到某个具体的服务,而具体的 url 则交给 Eureka 的服务发现机制去自动维护。这类的路由便称为面向服务的路由。
传统路由配置
单实例配置
单实例的路由转发经过 zuul.routes.<route>.path 与 zuul.routes.<route>.url 参数对的方式进行配置。
好比下面配置实现了对符合 /zuul-service/** 规则的请求路径转发到 http://localhost:8888/ 地址的路由规则
zuul.routes.zuul-service.path=/zuul-service/**zuul.routes.zuul-service.url=http://localhost:8888/
多实例配置
多实例的路由转发经过 zuul.routes.<route>.path 与 zuul.routes.<route>.service-id 参数对的方式进行配置,其中 service-id 是由用户手工命名的服务名称,配合 ribbon.listOfServers 参数实现服务与实例的维护:
好比下面配置实现了对符合 /my-service/** 规则的请求路径转发到 http://localhost:8888/ 和 http://localhost:9999/ 两个实例地址的路由规则。
zuul.routes.zuul-service.path=/zuul-service/**zuul.routes.zuul-service.service-id=zuul-servicezuul-service.ribbon.listOfServers=http://localhost:8888/,http://localhost:9999/
面向服务路由配置
默认规则
当咱们为 Spring Cloud Zuul 构建的 API 网关服务引入 Spring Cloud Eureka 以后,它会为 Eureka 中的每一个服务都自动建立一个默认路由规则:使用服务名做为 path 请求前缀。
好比 http://192.168.0.128:8888/order-service/order这个请求就会直接转发到 ORDER-SERVICE 服务实例上。
关闭默认规则
咱们能够经过zuul.ignored-services=order-service 配置须要忽略的微服务(多个微服务经过逗号隔开),这样就不会自动对其建立路由规则。
咱们也可使用zuul.ignored-services=* 对全部的服务都不自动建立路由规则。
路径匹配
不管是使用传统路由的配置方式仍是服务路由的配置方式,咱们都须要为每一个路由规则定义匹配表达式,也就是上面所说的path
参数。在Zuul中,路由匹配的路径表达式采用了Ant风格定义。
Ant风格的路径表达式使用起来很是简单,它一共有下面这三种通配符:
通配符 | 说明 |
---|---|
? | 匹配任意的单个字符 |
* | 匹配任意数量的字符 |
** | 匹配任意数量的字符,支持多级目录 |
通配符实例演示:
/user-service/?
它能够匹配/user-service/以后拼接一个任务字符的路径,好比:/user-service/a、/user-service/b、/user-service/c
/user-service/*
它能够匹配/user-service/以后拼接任意字符的路径,好比:/user-service/a、/user-service/aaa、/user-service/bbb。可是它没法匹配/user-service/a/b
/user-service/**
它能够匹配/user-service/*包含的内容以外,还能够匹配形如/user-service/a/b的多级目录路径
忽略表达式
经过path参数定义的ant表达式已经可以完成api网关上的路由规则配置功能,可是为了更细粒度和更为灵活地配置理由规则,zuul还提供了一个忽略表达式参数zuul.ignored-patterns。
该参数能够用来设置不但愿被api网关进行路由的url表达式。
注意:该参数在使用时还须要注意它的范围并非针对某个路由,而是对全部路由。因此在设置的时候须要全面考虑url规则,防止忽略了不应被忽略的url路径。
好比咱们启动order-service服务,访问:http://192.168.1.57:6069/order/index
可使用api网关路由:http://192.168.1.57:6069/order-service/order/index
在zuul-service中配置
spring: application: name: zuul-serviceeureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} prefer-ip-address: trueserver: port: 6069zuul:ignoredPatterns: /**/index/** routes: user-service: path: /user-service/** serviceId: user-service order-service: path: /order-service/** serviceId: order-servicelogging: level: com.netflix: debug
设置后若是访问 http://localhost:8888/order-service/index 将不会被正确路由,由于该路径符合zuul.ignored-patterns 参数定义的规则。而其余路径则不会有问题,好比http://localhost:8888/order-service/getOrderInfo。
路由前缀
Zuul经过zuul.prefix
参数来为路由规则增长前缀信息。
zuul: routes: user-service: path: /user-service/** serviceId: user-service order-service: path: /order-service/** serviceId: order-service prefix: /javaer123456
配置完前缀以后,以前访问路径都要增长 /javer123456前缀:
未加前缀时访问 user-service 服务:http://localhost:8888/user-service/hello
添加前缀后访问 hello-service 服务:http://localhost:8888/javaer123456/user-service/hello
Zuul经过strip-prefix代理前缀默认会从请求路径中移除,经过该设置关闭移除功能
当 stripPrefix=true 的时 (会移除)
(http://127.0.0.1:8888/javaer123456/user/list-> http://192.168.1.100:8080/user/list)
当stripPrefix=false的时(不会移除)
http://127.0.0.1:8888/javaer123456/user/list ->http://192.168.1.100:8080/javaer123456/user/list
本地跳转
在 Zuul 实现的 API 网关路由功能中,还支持 forward 形式的服务端跳转配置。
好比咱们在 API 网关项目中增长一个 /local/helloWorld 的接口
@RestControllerpublic class HelloController { @RequestMapping("/local/helloWorld") public String hello() { return "welcome to Java学习部落"; }}
而后在 增长一个本地跳转的路由规则(forward-local):
zuul.routes.forward-local.path=/forward-local/**zuul.routes.forward-local.url=forward:/local
当 API 网关接收到请求 /forward-local/helloWorld,它符合 forward-local 的路由规则,因此该请求会被 API 网关转发到网关的 /local/helloWorld请求上进行本地处理。
cookie与头信息
Spring Cloud Zuul 在请求路由时,经过zuul.sensitiveHeaders 参数定义,包括Cookie、Set-Cookie、Authorization 三个属性来过滤掉HTTP请求头信息中的一些敏感信息,防止它们被传递到下游的外部服务器。
可是若是咱们将使用了Spring Security、Shiro 等安全框架构建的 Web 应用经过 Spring Cloud Zuul 构建的网关来进行路由时,Cookie 信息没法传递,会致使没法实现 登陆和鉴权。
如何解决:
经过指定路由的参数来设置,仅对指定的web应用开启敏感信息传递
# 对指定路由开启自定义敏感头zuul.routes.<router>.customSensitiveHeaders=true# 将指定路由的敏感头信息设置为空zuul.routes.<router>.sensitiveHeaders=[这里设置要过滤的敏感头]
注意:指定路由的敏感头配置会覆盖掉全局设置
重定向问题
什么是Zuul的重定向问题?
咱们在浏览器中经过 Zuul 网关发起了认证服务,认证经过后会进行重定向到某个主页或欢迎页面。此时,咱们发现,在认证完成以后,可是发现重定向的这个欢迎页的 host 变成了这个认证服务的 host,而不是 Zuul 的 host,这是一个很严重的问题。
解决方法:
### 网关配置zuul: routes: demo-order: path: /do/** serviceId: demo-order stripPrefix: true sensitiveHeaders: Cookie,Set-Cookie,Authorization # 此处解决后端服务重定向致使用户浏览的 host 变成 后端服务的 host 问题 add-host-header: true
Zuul核心知识点---过滤器
过滤器能够说是zuul实现api网关功能最核心的部件,Zuul大部分功能都是经过过滤器来实现的。每个进入zuul的http请求都会通过一系列的过滤器处理链获得请求响应并返回给客户端。
咱们能够在ZuulFilter接口中看到定义的4个抽象方法
String filterType();int filterOrder();boolean shouldFilter();Object run();
这是个抽象方法也就表明了过滤器的四个核心概念
(filterType)类型Type:定义在路由流程中,过滤器被应用的阶段
(filterOrder)执行顺序Execution Order:在同一个Type中,定义过滤器执行的顺序
(shouldFilter)条件Criteria:过滤器被执行必须知足的条件
(run)动做Action:过滤器的具体逻辑。在该函数中,咱们能够实现自定义的过滤逻辑,来肯定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果以后,对处理结果作一些加工等。
类型Type
Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
(1) PRE:这种过滤器在请求被路由以前调用。咱们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
(3) POST:这种过滤器在路由到微服务之后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
核心过滤器详解
核心过滤器执行顺序
前置过滤器
1.ServletDetectionFilter
它的执行顺序为-3,是最早被执行的过滤器。
该过滤器老是会被执行,主要用来检测当前请求是经过Spring的DispatcherServlet处理运行的,仍是经过ZuulServlet来处理运行的。
它的检测结果会以布尔类型保存在当前请求上下文的isDispatcherServletRequest参数中,这样后续的过滤器中,咱们就能够经过RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法来判断请求处理的源头,以实现后续不一样的处理机制。
通常状况下,发送到api网关的外部请求都会被Spring的DispatcherServlet处理,除了经过/zuul/*路径访问的请求会绕过DispatcherServlet(好比以前咱们说的大文件上传),被ZuulServlet处理,主要用来应对大文件上传的状况。
另外,对于ZuulServlet的访问路径/zuul/*,咱们能够经过zuul.servletPath参数进行修改。
2.Servlet30WrapperFilter
它的执行顺序为-2,是第二个执行的过滤器,目前的实现会对全部请求生效,主要为了将原始的HttpServletRequest
包装成Servlet30RequestWrapper
对象。
3.FormBodyWrapperFilter
它的执行顺序为-1,是第三个执行的过滤器。该过滤器仅对两类请求生效,第一类是Context-Type为application/x-www-form-urlencoded的请求,第二类是Context-Type为multipart/form-data而且是由String的DispatcherServlet处理的请求(用到了ServletDetectionFilter的处理结果)。
而该过滤器的主要目的是将符合要求的请求体包装成FormBodyRequestWrapper对象。
4.DebugFilter
它的执行顺序为1,是第四个执行的过滤器,该过滤器会根据配置参数zuul.debug.request
和请求中的debug参数来决定是否执行过滤器中的操做。
而它的具体操做内容是将当前请求上下文中的debugRouting
和debugRequest
参数设置为true。
因为在同一个请求的不一样生命周期均可以访问到这二个值,因此咱们在后续的各个过滤器中能够利用这二个值来定义一些debug信息,这样当线上环境出现问题的时候,能够经过参数的方式来激活这些debug信息以帮助分析问题,另外,对于请求参数中的debug参数,咱们能够经过zuul.debug.parameter
来进行自定义。
5.PreDecorationFilter
执行顺序是5,是pre阶段最后被执行的过滤器,该过滤器会判断当前请求上下文中是否存在forward.do
和serviceId
参数,若是都不存在,那么它就会执行具体过滤器的操做(若是有一个存在的话,说明当前请求已经被处理过了,由于这二个信息就是根据当前请求的路由信息加载进来的)。
而当它的具体操做内容就是为当前请求作一些预处理,好比说,进行路由规则的匹配,在请求上下文中设置该请求的基本信息以及将路由匹配结果等一些设置信息等,这些信息将是后续过滤器进行处理的重要依据,咱们能够经过RequestContext.getCurrentContext()
来访问这些信息。
另外,咱们还能够在该实现中找到对HTTP头请求进行处理的逻辑,其中包含了一些耳熟能详的头域,好比X-Forwarded-Host
,X-Forwarded-Port
。
另外,对于这些头域是经过zuul.addProxyHeaders
参数进行控制的,而这个参数默认值是true,因此zuul在请求跳转时默认会为请求增长X-Forwarded-*
头域,包括X-Forwarded-Host
,X-Forwarded-Port
,X-Forwarded-For
,X-Forwarded-Prefix
,X-Forwarded-Proto
。
也能够经过设置zuul.addProxyHeaders=false
关闭对这些头域的添加动做。
路由过滤器
6.RibbonRoutingFilter
它的执行顺序为10,是route阶段的第一个执行的过滤器。
该过滤器只对请求上下文中存在serviceId参数的请求进行处理,即只对经过serviceId配置路由规则的请求生效。
而该过滤器的执行逻辑就是面向服务路由的核心,它经过使用ribbon和hystrix来向服务实例发起请求,并将服务实例的请求结果返回。
7.SimpleHostRoutingFilter
它的执行顺序为100,是route阶段的第二个执行的过滤器。
该过滤器只对请求上下文存在routeHost参数的请求进行处理,即只对经过url配置路由规则的请求生效。
而该过滤器的执行逻辑就是直接向routeHost参数的物理地址发起请求,从源码中咱们能够知道该请求是直接经过httpclient包实现的,而没有使用Hystrix命令进行包装,因此这类请求并无线程隔离和断路器的保护。
知道配置相似zuul.routes.user-service.url=http://localhost:8080/这样的底层都是经过httpclient直接发送请求的,也就知道为何这样的状况没有作到负载均衡的缘由所在。
8.SendForwardFilter
它的执行顺序是500,是route阶段第三个执行的过滤器。该过滤器只对请求上下文中存在的forward.do参数进行处理请求,即用来处理路由规则中的forward本地跳转装配。
后置过滤器
9.SendErrorFilter
它的执行顺序是0,是post阶段的第一个执行的过滤器。该过滤器仅在请求上下文中包含error.status_code参数(由以前执行的过滤器设置的错误编码)而且尚未被该过滤器处理过的时候执行。而该过滤器的具体逻辑就是利用上下文中的错误信息来组成一个forward到api网关/error错误端点的请求来产生错误响应。
10.SendResponseFilter
它的执行顺序为1000,是post阶段最后执行的过滤器,该过滤器会检查请求上下文中是否包含请求响应相关的头信息,响应数据流或是响应体,只有在包含它们其中一个的时候执行处理逻辑。
而该过滤器的处理逻辑就是利用上下文的响应信息来组织须要发送回客户端的响应内容。
如何禁用过滤器
Spring Cloud默认为Zuul编写并启用了一些过滤器,一些场景下,想要禁用掉部分过滤器,此时该怎么办呢?
只需设置zuul.<SimpleClassName>.<filterType>.disable=true
,便可禁用SimpleClassName
所对应的过滤器。
以过滤器org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter
为例,只需设置zuul.SendResponseFilter.post.disable=true
,便可禁用该过滤器。
Zuul自定义过滤器
由上文可知,咱们能够将Zuul的过滤器分为pre,route,post三大类型。本文咱们将自定义一个pre类型的过滤器。
首先要自定义一个过滤器,只须要完成如下几个步骤。
1.继承ZuulFilter
2.指定过滤类型、过滤顺序
3.是否执行这个过滤器、过滤内容
1.首先定一个抽象类 AbstractZuulFilter.java 继承ZuulFilter
package com.javaer.study.zuul.core.zuul.filter;import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.javaer.study.zuul.core.zuul.ContantValue;/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/public abstract class AbstractZuulFilter extends ZuulFilter { protected RequestContext context; @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); return (boolean) (ctx.getOrDefault(ContantValue.NEXT_FILTER, true)); } @Override public Object run() { context = RequestContext.getCurrentContext(); return doRun(); } public abstract Object doRun(); public Object fail(Integer code, String message) { context.set(ContantValue.NEXT_FILTER, false); context.setSendZuulResponse(false); context.getResponse().setContentType("text/html;charset=UTF-8"); context.setResponseStatusCode(code); context.setResponseBody(String.format("{\"result\":\"%s!\"}", message)); return null; } public Object success() { context.set(ContantValue.NEXT_FILTER, true); return null; }}
2.定义preFilter的抽象类,继承AbstractZuulFilter。指定过滤器类型为pre类型。
package com.javaer.study.zuul.core.zuul.filter.pre;import com.javaer.study.zuul.core.zuul.FilterType;import com.javaer.study.zuul.core.zuul.filter.AbstractZuulFilter;/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/public abstract class AbstractPreZuulFilter extends AbstractZuulFilter { @Override public String filterType() { return FilterType.pre.name(); }}
3.接着编写具体一个具体的限流过滤器
package com.javaer.study.zuul.core.zuul.filter.pre;import com.google.common.util.concurrent.RateLimiter;import com.syher.zuul.core.zuul.FilterOrder;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/public class RateLimiterFilter extends AbstractPreZuulFilter { private static final Logger LOGGER = LoggerFactory.getLogger(RateLimiterFilter.class); /** * 每秒容许处理的量是50 */ RateLimiter rateLimiter = RateLimiter.create(50); @Override public int filterOrder() { return FilterOrder.RATE_LIMITER_ORDER; } @Override public Object doRun() { HttpServletRequest request = context.getRequest(); String url = request.getRequestURI(); if (rateLimiter.tryAcquire()) { return success(); } else { LOGGER.info("rate limit:{}", url); return fail(401, String.format("rate limit:{}", url)); } }}
4.最后建立一个配置类,将下列过滤器托管给spring。
package com.javaer.study.zuul.core.config;import com.netflix.zuul.ZuulFilter;import com.syher.zuul.core.zuul.filter.pre.RateLimiterFilter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/@Configurationpublic class ZuulConfigure { /** * 自定义过滤器 * @return */ @Bean public ZuulFilter rateLimiterFilter() { return new RateLimiterFilter(); }}
Zuul 容错与回退
看过以前讲解什么是Hystrix,阿里技术最终面,遗憾的倒在Hystrix面前的文章的同窗们应该都知道,不少时候,不管是由于服务节点的重启,宕机仍是因为网络故障,咱们在访问某一个服务节点时可能会出现阻塞或者异常。
Zuul进行路由时候也会由于这些缘由出现异常。
此时若是咱们直接将异常信息展现给用户的话确定是很不友好的,咱们须要展现给用户的是用户能看的明白的形成访问失败的缘由。
这里咱们就能够用到Zuul的回退处理了。
SpringCloud中使用Hystrix实现微服务的容错与回退,其实Zuul默认已经整合了Hystrix。
实现Zuul添加回退
要实现Zuul添加回退,须要实现ZuulFallbackProvider接口,而后在实现类中,指定为哪一个微服务提供回退,并提供一个ClientHttpResponse做为回退响应。
package com.javaer.study.zuul;import com.alibaba.fastjson.JSONObject;import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.client.ClientHttpResponse;import org.springframework.stereotype.Component;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/@Componentpublic class ZuulFallBack implements FallbackProvider { /**为哪一个服务提供回退,*号表明全部服务**/ @Override public String getRoute() { return "order-service"; //根据服务id指定为哪一个微服务提供回退,能够用* 或者 null 表明全部服务// } /**回退响应**/ @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { /**回退时的状态码**/ @Override public HttpStatus getStatusCode() throws IOException { //请求网关成功了,因此是ok return HttpStatus.OK; } /**数字类型状态码**/ @Override public int getRawStatusCode() throws IOException { return HttpStatus.OK.value(); } /**状态文本**/ @Override public String getStatusText() throws IOException { return HttpStatus.OK.getReasonPhrase(); } /****/ @Override public void close() { } /**响应体**/ @Override public InputStream getBody() throws IOException { JSONObject json =new JSONObject(); json.put("state","501"); json.put("msg","后台接口错误"); //返回前端的内容 return new ByteArrayInputStream(json.toJSONString().getBytes("UTF-8")); } /**返回的响应头**/ @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); //设置头 return httpHeaders; httpHeaders.setContentType(MediaType.APPLICATION_JSON); } }; }}
注意:在Spring cloud Edgware版本以前,要想回退,需实现ZuulFallBackProvider接口,从Spring cloud Edgware版本以后,实现FallbackProvider接口。
总结
本文从什么是网关和为何要使用网关引入SpringCloud Zuul,介绍了什么是Zuul,Zuul的使用场景。而且分析了Zuul的核心架构以及Zuul请求处理的生命周期。而后着重的讲解了Zuul的路由和过滤器这两个在任何网关中都十分重要的组件。本文旨在带领你们对Zuul有一个全局的认识。在往后学习其它网关技术的时候也可以融会贯通,学习起来可以事半功倍。