Spring Cloud(七):服务网关zuul过滤器
上文介绍了Zuul的基本使用与路由功能,本文接着介绍Zuul的核心概念 —— Zuul过滤器(filter)。
Zuul的功能基本通过Zuul过滤器来实现(类比于Struts的拦截器,只是Struts拦截器用到责任链模式,Zuul则是通过FilterProcessor来控制执行),在不同的阶段,通过不同类型的过滤器来实现相应的功能。
Zuul过滤器
过滤器类型
zuul的过滤器根据对HTTP请求的不同处理阶段包括如下四种类型
- pre :在请求转发到后端目标服务之前执行,一般用于请求认证、确定路由地址、日志记录等
- route :转发请求,使用Apache HttpClient 或 Ribbon来构造对目标服务的请求
- post :在目标服务返回结果后对结果进行处理,比如添加响应头、收集统计性能数据等
- error :在请求处理的整个流程中如果出现错误,则会触发error过滤器执行,对错误进行处理
客户端请求经过zuul过滤器处理的流程如下图

zuul使用RequestContext来在过滤器之间传递数据,数据存于每个request的ThreadLocal,包含请求路由到哪里,错误,HttpServletRequest,HttpServletResponse 等这些数据都存储于RequestContext中。RequestContext 扩展了ConcurrentHashMap,所以我们可以根据需要将信息存于context中进行传递。
@EnableZuulProxy vs @EnableZuulServer
zuul提供了两个注解 @EnableZuulProxy, @EnableZuulServer,来启用不同的过滤器集合。@EnableZuulProxy 启用的过滤器 是@EnableZuulServer 的超集, 它包含了@EnableZuulServer 的所有过滤器,proxy主要多了一些提供路由功能的过滤器(可见@EnableZuulServer 不提供路由功能,作为server模式而不是代理模式运行)
@EnableZuulServer 注解启用的过滤器包括
| filter类型 | 实现类 | filter顺序值 | 功能说明 |
|---|---|---|---|
| pre | ServletDetectionFilter | -3 | 检测请求是否通过Spring Dispatcher,并在RequestContext 中添加一个key为isDispatcherServletRequest, 值为true(不通过则为false)的属性 |
| pre | FormBodyWrapperFilter | -1 | 解析Form data,为请求的下游进行重新编码 |
| pre | DebugFilter | 1 | 如果请求参数设置了debug,则会将RequestContext.setDebugRouting() ,RequestContext.setDebugRequest() 设置为ture |
| route | SendForwardFilter | 500 | 使用RequestDispatch servlet来转发请求,转发地址存于RequestContext中key为FilterConstants.FORWARD_TO_KEY的属性中,对于转发到当前应用的接口比较有用 |
| post | SendResponseFilter | 1000 | 将代理请求的响应内容写到当前的响应中 |
| error | SendErrorFilter | 0 | 如果RequestContext.getThrowable() 不为空,则会转发到/error,可以通过error.path来改变默认的转发路径/error |
@EnableZuulProxy 除了上面的过滤器,还包含如下过滤器
| filter类型 | 实现类 | filter顺序值 | 功能说明 |
|---|---|---|---|
| pre | PreDecorationFilter | 5 | 确定路由到哪里,如何路由,依赖提供的RouteLocator,同时也为下游请求设置多个与proxy相关的header |
| route | RibbonRoutingFilter | 10 | 使用ribbon,hystrix,以及内嵌的http client来发送请求,可在RequestContext中通过FilterConstants.SERVICE_ID_KEY 来找到路由Service的ID |
| route | SimpleHostRoutingFilter | 100 | 使用Apache httpClient来发送请求到一个预先确定的url,可通过RequestContext.getRouteHost()来获取urls |
由上可见@EnableZuulServer 注解并不包含往后端服务负载均衡地路由请求的代理功能,@EnableZuulProxy的PreDecorationFilter,RibbonRoutingFilter过滤器才能担当此任。PreDecorationFilter通过提供的DiscoveryClientRouteLocator 从 DiscoveryClient(如Eureka)与属性文件中加载路由定义, 为每个serviceId创建一个route,新服务添加进来,路由也会动态刷新。路由确定了,在RibbonRoutingFilter 中通过ribbon与hystrix结合来向后端目标服务发起请求,并进行负载均衡。过滤器的顺序值表示在同类型过滤器中的执行顺序,值越小越先执行。
自定义Zuul过滤器
自定义的zuul过滤器与框架自带过滤器类似,包括四部分
- 过滤器类型,包括pre, route, post
- 过滤器顺序,定义在同类型过滤器中的执行顺序,数值越小越先执行
- 是否执行过滤,通过一些条件判断来确定是否执行该过滤器
- 过滤器执行体,定义具体执行的操作
比如我们需要在Http请求头中设置一个值,供请求链路的下游环节访问,则可以自定义一个过滤器如下,
@Component
public class ReqIdPreFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; //在PreDecorationFilter过滤器之前执行
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("reqId", UUID.randomUUID().toString());
return null;
}
}
在请求的后续环节,比如后端服务的filter或接口中,则可直接从HttpServletRequest 获取该header值,如
@GetMapping("hello/reqId")
public String getReqId(HttpServletRequest request) {
return "hello-service返回:" + request.getHeader("reqId");
}
Zuul的错误处理
在zuul过滤器的生命周期中,如果任何一个环节抛出异常,则error过滤器会被执行,SendErrorFilter只有当RequestContext.getThrowable()不为null时才会运行,会设置javax.servlet.error.* 属性到request中,然后将请求转发到spring boot的error page, 默认为BasicErrorController实现的/error接口。 有时候我们需要将返回响应格式进行统一,而默认的/error接口实现可能不满足要求,则可以自定义/error接口。需要实现ErrorController 接口以使默认的BasicErrorController 失效。
@RestController
public class ZuulErrorController implements ErrorController {
@RequestMapping("/error")
public Map<String, String> error(HttpServletRequest request){
Map<String, String> result = Maps.newHashMap();
result.put("code", request.getAttribute("javax.servlet.error.status_code").toString());
result.put("message", request.getAttribute("javax.servlet.error.message").toString());
result.put("exception", request.getAttribute("javax.servlet.error.exception").toString());
return result;
}
@Override
public String getErrorPath() {
return "/error";
}
}
Zuul的服务降级
当调用服务出现超时或异常时,在zuul侧可提供回调进行服务降级,返回默认响应结果,如
@Component
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
return null; //指定这个回调针对的route Id,如果对所有route,则返回* 或null
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
Map<String, String> result = Maps.newLinkedHashMap();
result.put("code", "" + status.value());
String msg = HttpStatus.GATEWAY_TIMEOUT == getStatusCode() ? "请求服务超时" : "服务器内部错误";
result.put("message", msg);
return new ByteArrayInputStream(new ObjectMapper().writeValueAsString(result).getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
则当服务请求失败时,统一返回如下格式的响应
{
"code": "500",
"message": "服务器内部错误"
}
总结
本文主要对Zuul过滤器相关内容及自定义使用进行了介绍,同时对过滤器运行过程中异常的处理及服务调用失败的降级回调进行了简单说明。出于篇幅,开发过程中更具体的细节我们后续再继续探讨。
认真生活,快乐分享
欢迎关注微信公众号:空山新雨的技术空间
获取Spring Boot,Spring Cloud,Docker等系列技术文章

Spring Cloud(七):服务网关zuul过滤器的更多相关文章
- Spring Cloud Gateway 服务网关快速上手
Spring Cloud Gateway 服务网关 API 主流网关有NGINX.ZUUL.Spring Cloud Gateway.Linkerd等:Spring Cloud Gateway构建于 ...
- 二十一、springcloud(七)服务网关zuul
1.简介 Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,在微服务架构中,后端 ...
- Spring Cloud内置的Zuul过滤器详解
Spring Cloud默认为Zuul编写并启用了一些过滤器,这些过滤器有什么作用呢?我们不妨按照@EnableZuulServer.@EnableZuulProxy两个注解进行展开,相信大家对这两个 ...
- Spring Cloud (13) 服务网关-路由配置
传统路由配置 所谓传统路由配置方式就是在不依赖于服务发现机制情况下,通过在配置文件中具体制定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由.没有Eureka服务治理框架帮助的时候, ...
- Spring Cloud (12) 服务网关-基础
通过前几篇介绍,已经可以构建一个简单的微服务架构了,如下图: 通过eureka实现服务注册中心以及服务注册发现,通过ribbon或feign实现服务的消费以及负载均衡,通过spring cloud c ...
- Spring Cloud (14) 服务网关-过滤器
Spring Cloud Zuul作为网关所具备的最基本的功能:路由,还具备另外一个核心的功能:过滤器. 过滤器 通过Spring Cloud Zuul实现的路由功能,我们的微服务可以通过统一的API ...
- Spring Cloud Gateway服务网关
原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gatewa ...
- spring cloud:服务网关 Spring Cloud GateWay 入门
Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...
- Spring Cloud 之 服务网关
在微服务架构体系中,使用API 服务网关后的系统架构图如下: API服务网关的主要作用如下: 服务访问的统一入口 服务访问的负载均衡功能 服务访问的路由功能 在SpringCloud中,基于Netfl ...
随机推荐
- JavaScript-null与' '的区别
null代表的是空对象无地址,而' '则代表的是有地址,但是这个地址里面的内容为空
- 【转】Java实现折半查找(二分查找)的递归和非递归算法
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://wintys.blog.51cto.com/425414/94051 Java二分 ...
- 关于revit的外部扩展存储
最近被revit的外部扩展存储搞得死去活来,作为日后再次使用的预防针,此处随手留下印记,以作警示. 首先我们知道外部扩展存储ExtensibleStorage是revit提供给revit二次开发人员用 ...
- 机器学习——Java调用sklearn生成好的Logistic模型进行鸢尾花的预测
机器学习是python语言的长处,而Java在web开发方面更具有优势,如何通过java来调用python中训练好的模型进行在线的预测呢?在java语言中去调用python构建好的模型主要有三种方法: ...
- Flutter使用SingleTickerProviderStateMixin报错
最近在学习开发Flutter应用项目,在创建tabbar和tabview后,进行网络请求后显示顶部tab标签,设置TabController,并使class类实现SingleTickerProvide ...
- 微信小程序---自定义三级联动
在开发的很多电商类型的项目中,免不了会遇到三级联动选择地址信息,如果单纯的使用文本框给用户选择,用户体检可能就会差很多.今天我给大家整理了关于小程序开发利用picker-view组件和animatio ...
- cf - 429D
Iahub and Sorin are the best competitive programmers in their town. However, they can't both qualify ...
- Mysql的binlog日志与mysqlbinlog命令
binlog相关 MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志,它记录了所有的 DDL 和 DML 语句(除了数据查询语句select.show等),以事件形式记录,还包 ...
- url的分发
一.分发 补充:通过查看源码:可以通过二级路由include进行二次分发 位置:urls.py urlpatterns = [ path('admin/', admin.site.urls), pat ...
- [bzoj4872] [洛谷P3750] [六省联考2017] 分手是祝愿
Description Zeit und Raum trennen dich und mich. 时空将你我分开. \(B\) 君在玩一个游戏,这个游戏由 \(n\) 个灯和 \(n\) 个开关组成, ...