apache / apache/dubbo

Dubbo框架Filter排序过程不稳定问题

Open
#8,055 18 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
41.6k
Forks
26.4k
Avg merge
15h 13m
Merged PRs (30d)
4

Description

**环境:**
Dubbo version: 2.7.10
Operating System version: win10
Java version: oracle jdk 1.8.0_281

在调试dubbo框架时,发现若应用继承了Filter接口(org.apache.dubbo.rpc.Filter)则Filter的执行顺序可能会被打乱,之前同事已经就此问题在社区上提出过一个issue(#7757),我们也很荣幸看到社区中已给出详细的解决方案。但实际上关于Filter执行顺序,我们发现dubbo在对Filter进行排序时还存在一个排序算法不稳定的问题。
**问题描述:**
我们在研究order属性相同的filter的执行顺序可能会被打乱时(issue: #7757),曾经对MonitorFilter和ExecuteLimitFilter进行过before、after属性的设置(比如在MonitorFilter的@Activate中设置before={executelimit},在ExecuteLimitFilter中设置after={monitor}),但设置后发现并未生效。
定位到类ExtensionLoader:
![image](https://user-images.githubusercontent.com/48745148/122052862-ba981c80-ce18-11eb-8c42-5c38dcade18e.png)
注意到对Filter进行排序是通过调用方法Collections.sort(List list, Comparator comparator)来实现的,所使用的自定义比较器为ActivateCompartor,其compare(Object o1, Object o2)方法(部分)实现如下:
![image](https://user-images.githubusercontent.com/48745148/122053099-03e86c00-ce19-11eb-9e08-6c6b6ee21238.png)
compare(…)方法的实现过程分为两部分(排除o1、o2为null的情况),前半部分是根据before、after列表比较(记作:part1),后半部分是根据order属性值来比较(记作:part2),part1、part2除了在方法实现中有先后顺序之外,在整个filter序列的排序过程中实际上是相对独立的。由于对Filter进行排序是通过调用方法Collections.sort(List list, Comparator comparator)来实现的,此方法所采用的排序算法为Timsort算法,Timsort无疑是世界上最快的排序算法,它是基于插入排序和归并排序算法实现的,但它主要考虑的是排序效率,并不能使待排序序列中每两个元素间都能发生比较,在dubbo框架中filter排序场景下(用于排序的比较器有两个比较标准,before-after和order),**这将导致一个很严重的后果:最后排序结果可能既不是严格的before-after顺序,也不是严格的order顺序。**

**举例:**
为了简单起见,我们现在只考虑插入排序过程,插入排序原理为从序列的第一个元素起,假设前面的序列有序,把后面的元素插入到前面的正确位置中。现假设有5个filter:
F1(order = -1)、F2(order = -2, before={F5s})、F3(order = -3)、F4(order = -4)、F5(after={F2}),F5我本意是想指定它在F2之前执行,其它的我不关心,所以未设置其order(但实际上此时他的默认值为order = 0)。假设某一时刻刚好前四个filter已经排好了顺序,当前序列为**F4->F3->F2->F1**->F5,现在需要把最后一个元素F5插入到前四个元素之间的某个位置;根据插入排序规则,F5首先会与F1进行比较,进入比较器,首先走part1,发现F5中虽然配置了after属性但是与F1无关,所以part1是走不通的;接着走part2,由于F5的order未设置取默认值order=0,而F1是order=-1,显然0>-1,所以比较结果是F5>F1,所以插入排序过程到此结束,最终得到的序列就是**F4->F3->F2->F1->F5**。这显然不是我想要的结果,这也就意味着我设置的F5(after={F2})并未生效,因为**在整个排序过程中,F5和F2根本就没有发生过比较**。
上面是只考虑插入排序的情况下可能出现的情况,如果考虑归并过程,同样会出现类似的情况。这大概也就是我们最开始提到的我们对MonitorFilter和ExecuteLimitFilter进行before、after属性的设置却并未生效的原因(而且我们在对filter排序过程进行debug调试时,这两个filter之间也确实没有发生过比较)。希望我以上已经把问题描述的足够清楚了,本人英语水平有限,就不翻译成英文了,见谅。
我们也注意到在dubbo-2.7.11中filter排序过程被修改成了通过TreeMap来实现,但比较器ActivateCompartor维持不变,因此此问题实际上依然存在。

### **综上所述,我和团队的同事一致认为,dubbo框架中对filter进行初始化排序的过程是不严谨、不稳定的,dubbo开发者可能无法得到自己按order或before-after配置的预期顺序。即,在一个比较器中同时采用两个不同的标准进行比较时,无论外部采用的是什么排序算法,都无法保证元素间的两两比较,都可能导致最终排序结果异常。**

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.