规则引擎的设计

规则引擎

我要做一个需求是要实现一个规则引擎,目的呢就是要进行一系列规则的判断,比如是否有重复,是否超过频率,是否达到最早开始时间和最晚结束时间,不符合条件的都被过滤掉,过滤的规模在2000w,尽量在2小时内完成。

初步想法

最开始的想法就是单次过滤要非常的快。由于一个要进行这么多的判断,是否重复,是否超过频率。这种类型的判断,实际上是要进行历史的对比,所以这些信息只能存在内存里,才能保证单个判断是比较快的。如果单个判断是2ms,那么多个的判断就会在10ms内就有可能满足条件。对于历史的消息的存储想好了就用redis来存了。

引擎的设计

由于判断时,如是否重复,需要加锁,因为有多线程竞态条件,所以必须加锁,如果是直接锁判断的的方法的话,并发发送时估计性能不够。当时就想起来HashMap内部的实现,使用KEY的HASH把竞态的条件分布到不同的区去,在各自的区上加锁。把这个模块做成一个服务,供外部的来调用。但实现难度有点大,这个引擎得自己写啊。

灵机一动

后面突然灵机一动,可以用Redis的乐观锁来实现刚才说的竞态条件啊。使用它的乐观锁。流程大概如下:

watch key
    exists?
    multi
    if(exist)
        //这个时间有可能出现竞态,键被删除,他又不算重复了。
        return 重复
     else
         添加条目
         return 不重复
     exec

这样就既用Redis为存了数据,又利用它的乐观锁的特性实现了业务。突然发现这里应该深补下Redis乐观锁的性能哈,这样按每个key加一个锁,粒度很小了。

进一步加强

刚才用Redis虽然实现了这个,但日志数据2000W需要存来对比量还是蛮大,未来规则复杂了还有扩大的可能,且单个Redis还有单点故障,存在一个Redis里面还不是很安全,后面就想做个Redis集群吧。但是常用的集群插件如Codis,twenty什么的不支持高级命令如事务这些。但我这里的规则在业务上只会针对一个key的操作,所以这样的限制对我其实无意义。后面就自己用jedis支持了通过Key的hash实现的集群

关于hash算法

关于hash算法经管Redis服务器的同学的提点,说可以用一个一致性hash算法,又简单看了下jedis的分片实现源码,本来想自己改一个,后面在网上找到了一个类似的实现,将hash算法改为了jedis的算法。

实际上线情况

上线前压测单次判断是2ms,后面在预生产环境压测时是10ms,当时奇了怪了,找了半天结果发现原来是redis在生产环境配置过大,全在初始化(使用houseMD跟踪,全在init),所以很慢。当时大概的测试逻辑是200个线程同时对10000数进行判断重复。相当于每个数会有200个线程进行竞争的重复判断。测试环境大概7ms左右,生产环境大概2ms单次。后面竞态条件去掉,在生产环境单个判断也差不多要2ms,难道已是极限了?

小插曲

由于一般进行批量推送,单次判断约有200个消息,每个消息进行约5次规则判断,按2ms算,用时要2秒。当时要每次用户存的时候就判断,那么每次用户访问要2s.完全不能接受,后面就只有把这个流程放到其它地方了。可以用多线程批量调用的地方。

Leave a Comment.