在上篇中介绍了SpringCloud Zuul路由网关的基本使用版本,本篇则介绍基于SpringCloud(基于SpringBoot2.x,.SpringCloud Finchley版)中的路由网关的过滤器Filter以及异常处理的教程。html
过滤器概述git
Zuul的中心是一系列过滤器,可以在HTTP请求和响应的路由过程当中执行一系列操做。github
如下是Zuul过滤器的主要特征:spring
过滤器目前用Groovy编写,尽管Zuul支持任何基于JVM的语言。每一个Filter的源代码都写入Zuul服务器上的一组指定目录,这些目录会按期轮询更改。更新的过滤器从磁盘读取,动态编译到正在运行的服务器中,并由Zuul为每一个后续请求调用。json
过滤器类型与请求生命周期浏览器
Zuul大部分功能都是经过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。springboot
官网Wiki 提供的四种过滤器的生命周期图。bash
注:此段来之Netflix / zuul的官网Wiki,地址:github.com/Netflix/zuu…服务器
开发环境app
注:不必定非要用上述的版本,能够根据状况进行相应的调整。须要注意的是SpringBoot2.x之后,jdk的版本必须是1.8以上!
因为在上一篇中咱们已经完成了Zuul路由网关的基本功能实现,因此服务端这块咱们能够直接把以前的项目拿来直接使用,而后更改相应的名称以及相关代码便可。
这里咱们来编写自定义一个Filter实现类,看看该类是如何工做的。 在编写该类的时候,发现自定义的Filter类须要继承ZuulFilter
这个类,咱们查看该类的源码,发现了该类定义了两个抽象的方法,而且该类实现了IZuulFilter
该接口,该接口也定义了两个方法,咱们就来看看这几个方法究竟是干吗的吧。
ZuulFilter源码:
filterType
这个方法表示按类型对过滤器进行分类,分别是pre、post、route和error,在FilterConstants
这个常量类中已经进行定义了,其意义在上述的Filter请求的典型生命周期已经进行过说明了。 filterOrder
这个方法表示Filter执行的顺序,数值越小优先级越高。
IZuulFilter
shouldFilter
该方法表示是否执行该过滤器,也能够说是该过滤器的一个开关。 run
过滤器的具体逻辑。在该函数中,咱们能够实现自定义的过滤逻辑,来肯定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果以后,对处理结果作一些加工等。
看完上述的源码以后,这里咱们再来编写自定的Filter代码。 首先继承ZuulFilter
该类,而后实现里面的方法。 首先是shouldFilter
方法,这里咱们就直接返回true; 而后是filterType
,这里咱们就设置为前置过滤器,在请求被路由以前调用。 继而是filterOrder
,这里咱们就设置0; 最后是run
,这是过滤器的核心业务代码,这里咱们就简单一点,获取请求的url,若是该url携带了token,咱们就让他经过,不然直接拦截。 固然,咱们须要将该过滤类使用Bean注解使其生效。 那么代码以下:
自定义的Filter代码:
@Component
public class MyZuulFilter extends ZuulFilter{
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
ctx.addZuulResponseHeader("Content-type", "text/json;charset=UTF-8");
ctx.getResponse().setCharacterEncoding("UTF-8");
System.out.println("请求地址:"+request.getRequestURI());
String token = request.getParameter("token");
String msg="请求成功!";
if(token==null) {
ctx.setSendZuulResponse(false);
msg="请求失败!";
ctx.setResponseBody(msg);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return msg;
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Bean
public MyZuulFilter zuulFilter() {
return new MyZuulFilter();
}
}
复制代码
Zuul除了能够自定义过滤器以外,也能够对异常结果进行处理,以保持返回值一致。在进行Zuul使用的时候发现了在发生了异常以后,会调用SendErrorFilter
异常过滤器,对异常常常处理,同时重定向至/error这个路径中。因此若是咱们须要自定义对异常处理的话,继承SendErrorFilter
该类就能够实现了。咱们查看SendErrorFilter
源码,其实也是继承ZuulFilter
该类并实现里面的一些方法,作的自定义异常封装,其实也能够把SendErrorFilter
该类当作一个自定义的过滤器。
因为SendErrorFilter
是对ZuulFilter
类进行了二次封装,因此咱们自定义的Error代码只需继承SendErrorFilter
改为,而后实现其中的run方法便可。
自定义的Error代码:
@Component
public class MyErrorFilter extends SendErrorFilter{
@Override
public Object run() {
String msg="请求失败!";
try{
RequestContext ctx = RequestContext.getCurrentContext();
ExceptionHolder exception = findZuulException(ctx.getThrowable());
System.out.println("错误信息:"+exception.getErrorCause());
msg+="error:"+exception.getErrorCause();
HttpServletResponse response = ctx.getResponse();
response.setCharacterEncoding("UTF-8");
response.getOutputStream().write(msg.getBytes());
}catch (Exception ex) {
ex.printStackTrace();
ReflectionUtils.rethrowRuntimeException(ex);
}
return msg;
}
@Bean
public MyErrorFilter errorFilter() {
return new MyErrorFilter();
}
}
复制代码
这里咱们还须要禁用SendErrorFilter
过滤器,否则是不会使用咱们自定的异常过滤器的。
在application.properties
添加以下配置:
zuul.SendErrorFilter.error.disable=true
复制代码
这里顺便说下禁用过滤器的规则。组件实现的过滤器,知足执行条件时都是会执行的,若咱们想禁用某个过滤器时,能够在配置文件中配置。 规则:
zuul.<SimpleClassName>.<filterType>.disable=true
复制代码
说明:
SimpleClassName为类名,filterType过滤器类型
固然,若是以为上述的异常处理仍是不够优雅的话,可使用ControllerAdvice
注解进行全局异常处理,该注解的使用示例能够从我的的springboot项目中进行找到,地址:github.com/xuwujing/sp…
在以前的关于springcloud中SpringCloud学习系列之三----- 断路器(Hystrix)和断路器监控(Dashboard)这篇文章中讲解过服务的降级处理,其实这里的处理也是相似,也就是某个服务没法进行访问的时候,进行回退处理。 这里咱们自定义异常回退处理的代码相对而已也比较简单,只需实现FallbackProvider
该接口的方法既可。
该类的源码以下:
getRoute
该方法主要是指定须要回退服务的名称。 fallbackResponse
该方法提供基于执行失败缘由并进行回退响应。
了解以后该源码以后,咱们再来编写一个自定义异常回退处理的类。
自定义的Fallback代码:
@Component
public class MyFallback implements FallbackProvider {
private static final String SERVER_NAME="springcloud-zuul-filter-server2";
@Override
public String getRoute() {
return SERVER_NAME;
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
//标记不一样的异常为不一样的http状态值
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
//可继续添加自定义异常类
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
//处理
private ClientHttpResponse response(final HttpStatus status) {
String msg="该"+SERVER_NAME+"服务暂时不可用!";
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 {
return new ByteArrayInputStream(msg.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
@Bean
public MyFallback eurekaClientFallback() {
return new MyFallback();
}
}
复制代码
客户端这边,咱们能够把以前springcloud-zuul项目中的springcloud-zuul-server1
和springcloud-zuul-server2
拿来进行使用既可。
完成上述的代码开发后,咱们来进行springcloud-zuul
的一系列自定义过滤测试。 首先依次启动springcloud-zuul-filter-eureka
、springcloud-zuul-filter-gateway
、springcloud-zuul-filter-server1
和springcloud-zuul-filter-server2
这四个项目。其中9009是服务端springcloud-zuul-filter-gatewayr
的端口,9010是第一个客户端springcloud-zuul-filter-server1
的端口,9011是第二个客户端springcloud-zuul-filter-server2
的端口。
这里顺便说下路由网关的默认规则:http://ZUUL_HOST:ZUUL_PORT/微服务实例名(serverId)/**
,转发至serviceId对应的微服务。好比在浏览器输入:http://localhost:9009/springcloud-zuul-filter-server1/hello
地址, 它就会跳转访问到:http://localhost:9010/hello/
这个地址上。使用这个方式进行测试能够帮助咱们更好的了解本篇文章的实现目的。
完成上述的项目启动成功以后。
咱们首先在浏览器上输入:
http://localhost:9009/springcloud-zuul-filter-server1/hello?name=pancm
界面返回:
请求失败!
这里看到直接进行拦截了,并返回了相应的信息、
加上token以后在进行访问
http://localhost:9009/springcloud-zuul-filter-server1/hello?name=pancm&token=123
界面返回:
pancm,Hello World!
咱们按照咱们自定的规则进行访问以后,发现能够直接访问到咱们想要访问的服务上,所以该次测试也符合咱们的预期,达成了自定义过滤器的处理。
上述的功能测试ok以后,这里咱们中止掉springcloud-zuul-filter-server1
服务,而后在浏览器上输入:
http://localhost:9009/springcloud-zuul-filter-server1/hello?name=pancm&token=123
界面返回:
请求失败!error:GENERAL请求失败!error:GENERAL
能够看到此次测试也符合咱们的预期,达成了自定义异常的处理。
这里咱们再来中止掉springcloud-zuul-filter-server2
服务,而后在浏览器上输入:
http://localhost:9009/springcloud-zuul-filter-server2/hi?name=pancm&token=123
界面返回:
该springcloud-zuul-filter-server2服务暂时不可用!
能够看到此次测试也符合咱们的预期,达成了 自定义异常回退处理的处理。这里也顺便说下,自定义该服务的异常和自定义异常回退处理最好不要在同一个服务同时使用,若是同时使用,会优先进行自定义异常回退处理的处理。
参考: github.com/Netflix/zuu… cloud.spring.io/spring-clou… www.itmuch.com/spring-clou… blog.lqdev.cn/2018/10/17/…
基于SpringBoot2.x、SpringCloud的Finchley版本开发的地址:github.com/xuwujing/sp…
若是感受项目不错,但愿能给个star,谢谢!
原创不易,若是感受不错,但愿留言推荐!您的支持是我写做的最大动力! 版权声明: 做者:虚无境 博客园出处:www.cnblogs.com/xuwujing CSDN出处:blog.csdn.net/qazwsxpcm 我的博客出处:www.panchengming.com