有一个DNS服务器项目(其实是最近在做的一个智能DNS项目)的过滤模块,现有如下需求:1.能准确判断出用户IP地址是否合法,及用户IP是否位于黑名单中;2.用户请求查询的主机名是否合法,机主机名是否位于黑名单中。要实现对以上用户请求过滤。后期可能还要实现对ddos攻击请求的过滤。由于前一段时间了解了一下责任链模式,所以决定用责任链模式实现该模块。类结构图如下所示(图画的不一定准确,凑合着看吧):
图中有一个抽象类Filter,他有一个抽象方法Validate(),用于实现对请求的过滤。其他类均继承自Filter类。其中IPFilter实现对IP过滤,DomainNameFilter实现对主机名的过滤。DDOsFilter实现对DDOs请求过滤,暂时未使用。FilterChain中有一个List<Filter> 类型对象filterChain用于存放各个过滤器。还有一个Add()方法用来向filterChain中添加过滤器。
现在由于没有新的需求,采用的是静态的去初始化filterChain。方法如下:
2
3 {
4 filterChain . Add ( filter ) ;
5 return this ;
6 }
7
然后,可以像下面一样去调用这个方法:
2 fc . Add ( new IPFilter ( ) ) . Add ( new DomainNameFilter ( ) ) . Add ( new DDosFilter ( ) ) ;
3
OK,现在只需调用fc.Validate(),就可以实现对IP和主机名的过滤。
似乎,我们已经实现该模块的功能了,而且用着也挺简单,调用时完全不需要知道过滤具体是怎么实现的。很完美,所以经测试之后我们将系统发布到服务器上开始工作。
但是,我们似乎忽略了一点。假如现在我们发现有用户发送恶意请求企图攻击我们的服务器,导致我们的服务器不能正常的为其他用户提供服务。我们当然不能容忍这种行为,更不能让攻击者得逞。所以我们要将DDosFilter也投入使用来时我们的系统更加强壮。
What should we do?
我们需要修改代码。如上面的fc.Add().Add().Add(),我们可以再在后面加一个Add().
OK,问题解决了。
假如,现在又有新的需求,主机名过滤不在那么重要了,要将其去掉。
怎么做?
正如上面我们需要修改代码来完成工作。但是慢慢的我们会发现,我们每次都需要修改代码,而且每次只是需要修改一丁点儿的代码。但是我们的系统已经正式发布了,每次只为这么一点小事就修改代码,会显示出我们的系统不够强壮。而且每次的一丁点的修改,也使我们的工作看起来似乎没什么意义。
我们寻求更加简便的方法来完成对系统的更改。于是我们想到了配置文件。我们可以将要使用的过滤器写入配置文件中,这样只需修改配置文件就可以完成对系统的更改,而无需修改代码。
最近,看了看反射(虽然课堂上老师说只是了解即可,可我的直觉告诉我这个东西可以多了解一些,它很有用),它完全可胜任这一工作。当然还会有其他方法。但本文只探讨用反射机制实现。于是产生了如下面的代码:
2
3 {
4 string fileContent = File . ReadAllText ( " Filter.config " ) ;
5 string [ ] filters = fileContent . Trim ( ) . Split ( ' ; ' ) ;
6 Assembly ass = Assembly . GetExecutingAssembly ( ) ;
7 Type [ ] types = ass . GetTypes ( ) ;
8 object obj = null ;
9 foreach ( var item in filters )
10 {
11 try
12 {
13 obj = ass . CreateInstance ( item . Trim ( ) ) ;
14 }
15 catch ( Exception )
16 {
17 continue ;
18 }
19 if ( obj ! = null )
20 filterChain . Add ( ( Filter ) obj ) ;
21 }
22 return this ;
23 }
24
其中,需要加载的过滤器放在filter.config这个文件中。格式如下:
Filter.IPFilter;Filter.DomainNameFilter;Filter.DDosFilter
从配置文件中可以读出过滤器的名称,然后通过反射构造出对应过滤器的对象,然后将对象加入filterChain中,从而实现filterChain的动态初始化。
对于过滤规则,一个系统有一组就够了,所以可以进一步将filterChain声明为 private static List<Filter> 类型,然后在类构造器而不是实例构造器中去动态初始化它。这样在过滤时就完全不需要去考虑初始化过滤链的事儿了。当过滤需求放生变化时,只需修改配置文件,然后重启一下服务即可。进而摆脱频繁修改代码的烦恼。
本文转自HDDevTeam 51CTO博客,原文链接:http://blog.51cto.com/hddev/622944,如需转载请自行联系原作者