上文介绍了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过滤器与框架自带过滤器类似,包括四部分

  1. 过滤器类型,包括pre, route, post
  2. 过滤器顺序,定义在同类型过滤器中的执行顺序,数值越小越先执行
  3. 是否执行过滤,通过一些条件判断来确定是否执行该过滤器
  4. 过滤器执行体,定义具体执行的操作

比如我们需要在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过滤器的更多相关文章

  1. Spring Cloud Gateway 服务网关快速上手

    Spring Cloud Gateway 服务网关 API 主流网关有NGINX.ZUUL.Spring Cloud Gateway.Linkerd等:Spring Cloud Gateway构建于 ...

  2. 二十一、springcloud(七)服务网关zuul

    1.简介 Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,在微服务架构中,后端 ...

  3. Spring Cloud内置的Zuul过滤器详解

    Spring Cloud默认为Zuul编写并启用了一些过滤器,这些过滤器有什么作用呢?我们不妨按照@EnableZuulServer.@EnableZuulProxy两个注解进行展开,相信大家对这两个 ...

  4. Spring Cloud (13) 服务网关-路由配置

    传统路由配置 所谓传统路由配置方式就是在不依赖于服务发现机制情况下,通过在配置文件中具体制定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由.没有Eureka服务治理框架帮助的时候, ...

  5. Spring Cloud (12) 服务网关-基础

    通过前几篇介绍,已经可以构建一个简单的微服务架构了,如下图: 通过eureka实现服务注册中心以及服务注册发现,通过ribbon或feign实现服务的消费以及负载均衡,通过spring cloud c ...

  6. Spring Cloud (14) 服务网关-过滤器

    Spring Cloud Zuul作为网关所具备的最基本的功能:路由,还具备另外一个核心的功能:过滤器. 过滤器 通过Spring Cloud Zuul实现的路由功能,我们的微服务可以通过统一的API ...

  7. Spring Cloud Gateway服务网关

    原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gatewa ...

  8. spring cloud:服务网关 Spring Cloud GateWay 入门

    Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...

  9. Spring Cloud 之 服务网关

    在微服务架构体系中,使用API 服务网关后的系统架构图如下: API服务网关的主要作用如下: 服务访问的统一入口 服务访问的负载均衡功能 服务访问的路由功能 在SpringCloud中,基于Netfl ...

随机推荐

  1. Sample Code之Web scene-slides

    这是我的第一篇随笔,在开始正文前说几句. 这个系列会记录我学习Arcgis js API 4.10的全过程,希望能对自己也对其他有需要的人有帮助.很多时候上网看一些大神的帖子会感到一头雾水,一是自己水 ...

  2. C#实现DataTable转TXT文件

    实现DataTable转TXT文件代码如下: public ExecutionResult DataTableToTxt(DataTable vContent, string vOutputFileP ...

  3. java 大数的学习

    import java.math.*; import java.util.*; public class study { public static void main(String[] args) ...

  4. 测试必备之Java知识(四)—— 线程相关

    线程相关 Java多线程实现方式 继承Thread,实现Runnable接口,实现Callable接口(能抛异常且有返回值,不常用) 为什么有了继承Thread方式还要有Runnable接口方式 实现 ...

  5. Redis Sentinel 高可用机制

    内容目录: Sentinel 如何工作的? 核心配置项 怎么选出新 master 的? Sentinel 有多个,具体谁来执行故障转移? Sentinel 是怎么发现 slave 和其他 sentin ...

  6. webpack构建工具初始化并运行简单的demo

    webpack官网:https://webpack.js.org/ webpack是构建工具 安装webpack的前提:node,npm要安装 初始化项目 首先是初始化项目,创建一个文件夹,并且进入文 ...

  7. .NetCore自定义WebAPI返回Json的格式大小写的三种方式

    .NetCore的Controller/WebAPI可以帮我们将返回结果自动转换为Json格式给前台,而且可以自由设定格式(大写.小写.首字母大写等),我总结了三种方法,对应三种灵活度,供大家参考 ( ...

  8. 最强PostMan使用教程

    最近需要测试产品中的REST API,无意中发现了PostMan这个chrome插件,把玩了一下,发现postman秉承了一贯以来google工具强大,易用的特质.独乐乐不如众乐乐,特此共享出来给大伙 ...

  9. typescript step by step

    如果有本 typescript的书 这个名字不错 啊 step one

  10. 【原创】CentOS8双网卡绑定

    1. NAT网络配置(所有服务器): # yum install bash-completion # cd /etc/sysconfig/network-scripts/ bond0配置: # vim ...