zuul如果两个filter的order一样,是如何排序的?

苏三说技术

最近有个网友问了一个问题,zuul中如果两个filter的order一样,是如何排序的?引起了我的兴趣,特地去阅读了它的源码。

如果你有使用过springcloud应该听说过zuul,它的定位是分布式微服务中的API网关服务,当然后面可能要被gateway替代了。zuul是一个L7应用程序网关,提供了动态路由,监视,弹性,安全性等功能。zuul的大部分功能是通过filter实现的。

image.png

zuul定义了四种不同生命周期的filter

image.png

为了方便操作,zuul内置了一些filter,这些filter主要通过@EnableZuulServer@EnableZuulProxy注解开启相关功能。@EnableZuulServer注解开启的filter功能如下:

image.png

@EnableZuulProxy注解除了开启上面这些filter功能之外,还开启了如下的功能:

image.png

只需继承ZuulFilter类,实现它的filterTypefilterOrdershouldFilterrun方法即可,具体实现可参考如下代码:

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;
    }
}

上面的四个方法有哪些作用呢?

image.png

需要注意的是,要想使zuul的功能生效,切记要在springboot启动类上定义@EnableZuulServer@EnableZuulProxy注解,表示开启zuul的功能。

先看看所有的zuulFilter在哪里执行的,谜底就在FilterProcessor类的runFilters方法中。

image.png

该方法很简单,先获取所有zuulFilter,然后遍历所有zuulFilter,调用processZuulFilter方法执行具体的zuulFilter,然后将执行结果返回。

我们重点看看这个方法

FilterLoader.getInstance().getFiltersByType(sType);

该方法的具体逻辑
image.png

  1. 根据filterType从缓存中获取filter集合,如果缓存中有直接返回
  2. 如果缓存中没有,则创建filter集合,将所有filter中跟filterType的filter添加到filter集合中。
  3. 排序filter集合
  4. 将新创建的filter集合放入缓存。

从上面可以看出filter的排序是通过如下方法执行的:

Collections.sort(list);

该方法底层其实是通过listsort方法实现的image.png

看看ArrayListsort方法,传入的Comparator为null

image.png
它的底层又是通过Arrays类的静态方法sort实现的

image.png
由于上一步Comparator为null,则会执行sort方法。

image.png
该方法是通过ComparableTimSort类的sort方法实现的,这个方法是最核心的方法了
image.png

我们可以看到该方法其实是通过binarySort二分查找排序的。

image.png
通过compareTo方法比较大小。

我们回头再看看ZuulFilter

image.png
它实现了Comparable接口,重写了compareTo方法

image.png

所以,看到这里我们可以得出结论:ZuulFilter是通过Integercompare方法比较filterOrder参数值大小来排序的。

我们看看Integercompare方法具体的逻辑!
image.png

如果x==y,则返回0,x<y,则返回 -1,否则返回1 前面在二分查找中,只有x<y时,才会交换位置。
image.png

看到这里,我们得出这样的结论,如果filterOrder一样,则Collections.sort(list);排序时不交换位置,这按照ZuulFilter默认加载顺序。那么,ZuulFilter的默认加载顺序是怎么样的?
image.png

它是通过getAllFilters方法获取ZuulFilter集合,该方法其实返回的是名称为filtersConcurrentHashMapvalues,即返回Set集合,是无序的。

  • 重要的事情说三遍:如果filterOrder一样,ZuulFilter是无序的。
  • 重要的事情说三遍:如果filterOrder一样,ZuulFilter是无序的。
  • 重要的事情说三遍:如果filterOrder一样,ZuulFilter是无序的。

所以,filterOrder切记不要定义相同的,不然可能会出现无法预知的执行结果。

自定义排序其实有两种方法

  • 实现Comparable接口,重写compareTo方法,
  • 实现Comparator接口,重写compare方法

    如果要使用Collections.sort(list);排序,它默认用的是第一种方法,上面的filterOrder之所以可以排序,是因为Integer实现了Comparable接口,重写了compareTo方法

image.png

如果想自己定义排序规则可以通过实现Comparator接口,重写compare方法。

Collections.sort(list,new Comparator<Integer>(){
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});

它的底层也是通过二分查找实现的
image.png

那么这两种方法有什么区别呢?

  • Comparable接口位于java.lang包下,而Comparator接口位于java.util包下。
  • Comparable接口是内部比较器,一个类如果想要使用Collections.sort(list) 方法进行排序,则需要实现该接口
  • Comparator接口是外部比较器用于对那些没有实现Comparable接口或者对已经实现的Comparable中的排序规则不满意进行排序.无需改变类的结构,更加灵活。

zuul中是通过filterOrder参数的大小排序的,而在spring中是通过@Order注解排序的。

image.png
默认情况下,如果不指定value值,则value是Integer的最大值。由于排序规则是value越小,则排在越靠前,所以如果不指定value值,则它排在最后。
image.png
spring是通过OrderComparator类排序的,它实现了Comparator接口,它的doCompare方法实现的排序。

image.png
最终也是调用Integer类的compare方法,该方法前面已经介绍过了。

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,或者点赞、转发、在看。在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多大厂的前辈交流和学习。
image.png

阅读 247

公众号:「苏三说技术」 维护者目前就职于某知名互联网公司,从事开发、架构和部分管理工作。实战经验丰富,对jdk、spring、springboot、springcloud、mybatis等开源框架源码有一定研究,欢迎关注,和我一起交流。

121 声望
4 粉丝
0 条评论

公众号:「苏三说技术」 维护者目前就职于某知名互联网公司,从事开发、架构和部分管理工作。实战经验丰富,对jdk、spring、springboot、springcloud、mybatis等开源框架源码有一定研究,欢迎关注,和我一起交流。

121 声望
4 粉丝
宣传栏