引言
最近有个网友问了一个问题,zuul中如果两个filter的order一样,是如何排序的?引起了我的兴趣,特地去阅读了它的源码。
zuul是干什么的
如果你有使用过springcloud应该听说过zuul,它的定位是分布式微服务中的API网关服务,当然后面可能要被gateway替代了。zuul是一个L7应用程序网关,提供了动态路由,监视,弹性,安全性等功能。zuul的大部分功能是通过filter实现的。
zuul定义了四种不同生命周期的filter
![](https://img-blog.csdnimg.cn/img_convert/9eb21b5e9584bbecb8092739fa054bcb.png)
为了方便操作,zuul内置了一些filter,这些filter主要通过@EnableZuulServer
和@EnableZuulProxy
注解开启相关功能。@EnableZuulServer
注解开启的filter功能如下:
![](https://img-blog.csdnimg.cn/img_convert/862982e43f3d81b20a3047436a212848.png)
@EnableZuulProxy
注解除了开启上面这些filter功能之外,还开启了如下的功能:
![](https://img-blog.csdnimg.cn/img_convert/caabd4221212842d434822add35a80b6.png)
如何自定义filter
只需继承ZuulFilter
类,实现它的filterType
、filterOrder
、shouldFilter
和 run
方法即可,具体实现可参考如下代码:
public class LogFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return RequestContext.getCurrentContext().sendZuulResponse();
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
log.info("zuul pre filter-->" + request.getRequestURL() + "-->" + request.getMethod());
return null;
}
}
上面的四个方法有哪些作用呢?
方法名称 | 作用 |
---|---|
filterType | filter类型,包含:pre、routing、post和error四种类型 |
filterOrder | 排序,该值越小,filter越早执行 |
shouldFilter | 开关,表示是否需要执行该filter |
run | filter具体的功能方法 |
需要注意的是,要想使zuul的功能生效,切记要在springboot启动类
上定义@EnableZuulServer
或@EnableZuulProxy
注解,表示开启zuul的功能。
filterOrder是如何排序的
先看看所有的zuulFilter在哪里执行的,谜底就在FilterProcessor
类的runFilters
方法中。
![](https://img-blog.csdnimg.cn/img_convert/90c3cf9e9e3937e29739b6216f25c4cd.png)
该方法很简单,先获取所有zuulFilter,然后遍历所有zuulFilter,调用processZuulFilter
方法执行具体的zuulFilter,然后将执行结果返回。
我们重点看看这个方法
FilterLoader.getInstance().getFiltersByType(sType);
该方法的具体逻辑
根据filterType从缓存中获取filter集合,如果缓存中有直接返回
如果缓存中没有,则创建filter集合,将所有filter中跟filterType的filter添加到filter集合中。
排序filter集合
将新创建的filter集合放入缓存。
从上面可以看出filter的排序是通过如下方法执行的:
Collections.sort(list);
该方法底层其实是通过list
的sort
方法实现的
看看ArrayList
的sort
方法,传入的Comparator
为null
它的底层又是通过
Arrays
类的静态方法sort
实现的
由于上一步
Comparator
为null,则会执行sort
方法。
该方法是通过
ComparableTimSort
类的sort
方法实现的,这个方法是最核心的方法了
我们可以看到该方法其实是通过
binarySort
二分查找排序的。
通过
compareTo
方法比较大小。
我们回头再看看ZuulFilter
类
![](https://img-blog.csdnimg.cn/img_convert/691a3e0a476988b3a191009086795bf1.png)
它实现了Comparable
接口,重写了compareTo
方法
![](https://img-blog.csdnimg.cn/img_convert/476b7e9c0bcfede3d5cb27f5bb4eb88e.png)
所以,看到这里我们可以得出结论:ZuulFilter
是通过Integer
的compare
方法比较filterOrder
参数值大小来排序的。
如果filterOrder一样如何排序?
我们看看Integer
的compare
方法具体的逻辑
如果x==y,则返回0,x<y,则返回 -1,否则返回1 前面在二分查找中,只有x<y时,才会交换位置。看到这里,我们得出这样的结论,如果
filterOrder
一样,则Collections.sort(list);
排序时不交换位置,这按照ZuulFilter
默认加载顺序。那么,ZuulFilter的默认加载顺序是怎么样的?
它是通过
getAllFilters
方法获取ZuulFilter
集合,该方法其实返回的是名称为filters
的ConcurrentHashMap
的values
,即返回Set
集合,是无序的。
重要的事情说三遍:如果filterOrder一样,ZuulFilter是无序的。
重要的事情说三遍:如果filterOrder一样,ZuulFilter是无序的。
重要的事情说三遍:如果filterOrder一样,ZuulFilter是无序的。
所以,filterOrder切记不要定义相同的,不然可能会出现无法预知的执行结果。
两种排序方法
自定义排序其实有两种方法:
实现Comparable接口,重写compareTo方法,
实现Comparator接口,重写compare方法
如果要使用
Collections.sort(list);
排序,它默认用的是第一种方法,上面的filterOrder
之所以可以排序,是因为Integer
实现了Comparable
接口,重写了compareTo
方法
![](https://img-blog.csdnimg.cn/img_convert/66fb240978d83a39025e9465d86dfbff.png)
如果想自己定义排序规则可以通过实现Comparator
接口,重写compare
方法。
Collections.sort(list,new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
它的底层也是通过二分查找实现的
那么这两种方法有什么区别呢?
Comparable接口位于java.lang包下,而Comparator接口位于java.util包下。
Comparable接口是内部比较器,一个类如果想要使用Collections.sort(list) 方法进行排序,则需要实现该接口
Comparator接口是外部比较器用于对那些没有实现Comparable接口或者对已经实现的Comparable中的排序规则不满意进行排序.无需改变类的结构,更加灵活。
彩蛋
zuul
中是通过filterOrder
参数的大小排序的,而在spring
中是通过@Order
注解排序的。
默认情况下,如果不指定value值,则value是Integer的最大值。由于排序规则是value越小,则排在越靠前,所以如果不指定value值,则它排在最后。
spring
是通过OrderComparator
类排序的,它实现了Comparator
接口,它的doCompare
方法实现的排序。
最终也是调用
Integer
类的compare
方法,该方法前面已经介绍过了。
热门内容:
最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡