我应该在这种情况下使用Drools吗?

我将使用大学的图书馆系统来解释我的用例。 学生在图书馆系统中注册并提供他们的个人资料:性别,年龄,部门,以前完成的课程,当前注册的课程,已经借阅的书籍等。图书馆系统中的每本书都将根据学生的个人资料定义一些借阅规则,例如,计算机算法的教科书只能由目前在该课程注册的学生借用; 另一本教科书只能由数学系的学生借用; 也可能有规则,学生最多只能借2本电脑网络书。 由于借用规则,当学生在图书馆系统中搜索/浏览时,他只能看到他可以借用的书籍。 因此,该要求实际上归结为有效生成学生有资格借阅的书籍清单。

以下是我使用Drools对设计进行设想的方法 – 每本书都有一个规则,对学生档案有一些字段限制作为LHS,书规则的RHS只是将书籍ID添加到全局结果列表,然后是所有书籍规则被加载到RuleBase中。 当学生搜索/浏览图书馆系统时,会从RuleBase创建无状态会话,并且学生的个人资料被断言为事实,然后学生可以借用的每本书都会触发其图书规则并获得完整的图书清单。学生可以在全球结果列表中借阅。

一些假设:图书馆将处理数百万本书; 我不认为图书规则太复杂,平均每条规则最多3个简单的字段限制; 系统需要处理的学生数量在100K范围内,因此负载相当重。 我的问题是:如果加载了一百万本图书规则,Drools会占用多少内存? 所有这些百万条规则的解雇速度有多快? 如果Drools是合适的,我想听听一些有经验的用户设计这样一个系统的最佳实践。 谢谢。

首先,不要为每本书制定规则。 制定限制规则 – 定义的限制比书籍少得多。 这将对运行时间和内存使用量产生巨大影响。

通过规则引擎运行大量书籍将是昂贵的。 特别是因为您不会向用户显示所有结果:每页只有10-50。 想到的一个想法是使用规则引擎来构建一组查询条件。 (我实际上不会这样做 – 见下文。)

这就是我的想法:

rule "Only two books for networking" when Student($checkedOutBooks : checkedOutBooks), Book(subjects contains "networking", $book1 : id) from $checkedOutBooks, Book(subjects contains "networking", id != $book1) from $checkedOutBooks then criteria.add("subject is not 'networking'", PRIORITY.LOW); end rule "Books allowed for course" when $course : Course($textbooks : textbooks), Student(enrolledCourses contains $course) Book($book : id) from $textbooks, then criteria.add("book_id = " + $book, PRIORITY.HIGH); end 

但我实际上不会这样做!

这就是我改变问题的方法:不向用户显示书籍是一种糟糕的体验。 用户可能想要仔细阅读书籍以查看下次要获得的书籍。 出示书籍,但不允许结帐限制书籍。 这样,每个用户一次只能有1-50本书来运行规则。 这将是非常活泼的。 以上规则将成为:

 rule "Allowed for course" activation-group "Only one rule is fired" salience 10000 when // This book is about to be displayed on the page, hence inserted into working memory $book : Book(), $course : Course(textbooks contains $book), Student(enrolledCourses contains $course), then //Do nothing, allow the book end rule "Only two books for networking" activation-group "Only one rule is fired" salience 100 when Student($checkedOutBooks : checkedOutBooks), Book(subjects contains "networking", $book1 : id) from $checkedOutBooks, Book(subjects contains "networking", id != $book1) from $checkedOutBooks, // This book is about to be displayed on the page, hence inserted into working memory. $book : Book(subjects contains "networking") then disallowedForCheckout.put($book, "Cannot have more than two networking books"); end 

我在使用activation-group来确保只触发一个规则,以及确保按我希望的顺序触发它们的突出性。

最后, 保持缓存规则 。 Drools允许 – 并建议 – 只将规则加载到知识库中一次,然后从中创建会话。 知识库昂贵,会话便宜。

我对Drools(或一般的规则引擎)的体验是,如果用户对规则的可见性很重要,或者如果对规则进行快速更改而不使其成为编码项目很重要,或者如果规则集合如此是非常大的,因此难以管理,思考和分析代码(所以你会让商界人士要求技术人员去阅读代码并告诉他们在情况X中会发生什么)。

话虽这么说,规则引擎可能是一个瓶颈。 它们不会运行任何接近代码性能的东西,因此您需要在架构上预先管理它。 在这个特定的情况下,肯定有一个数据库,你可以添加到性能问题,数据库将返回一个查询比你在代码中分析整个集合快得多。

我绝对不会通过制作一百万个规则对象来实现它,而是我会制作一个可以分配多本书的书籍类型,并针对书籍类型运行规则,然后只显示允许类型的书籍。 这样,您可以加载类型,通过规则引擎传递它们,然后将允许的类型推送到数据库端的查询,该查询将提取允许类型中的书籍列表。

类型变得有点复杂,因为在实践中,一本书可能有两种类型(如果你正在学习某门课程,或者一般来说,如果你是该部门的一部分,则允许),但这种方法应该仍然适用。

我的问题是:如果加载了一百万本图书规则,Drools会占用多少内存? 所有这些百万条规则的解雇速度有多快?

你的电脑有多快,你有多少记忆? 从某种意义上说,你只能通过建立一个概念certificate并用适当数量的(随机生成的)测试数据填充它来找到答案。 我的经验是,Drools比你想象的要快,并且你必须非常了解底层的东西,以便能够预测什么会让它变慢。

请注意,您正在谈论一百万个规则会话事实 (即Book对象),而不是一百万个规则。 只有少数规则,不会花很长时间。 可能很慢的部分是插入百万个对象,因为Drools需要决定将哪些规则放在议程中以用于每个新事实。

令人遗憾的是,我们没有人能够通过一百万个事实得到某些特定设置的答案。

至于实现,我的方法是为学生想要签出的每本书插入一个Book对象,收回不允许的书,以及查询以获取剩余(允许的)Book对象,以及另一个查询到得到原因列表。 或者,使用RequestedBook对象,这些对象具有可在规则中设置的其他boolean allowedString reasonDisallowed属性。

每当我们查看大型数据集时(这个问题是关于…… Drools是否适合大型数据集),请在框外思考(如下)。 每当我们谈论“数百万个对象”或类似的log-N类型问题时,我认为他们所讨论的工具不一定是问题所在。 所以,是的,可以使用Drools(或JBoss Rules),但这只会在某种情况下有意义……

当你有任何log-N(交叉引用大数据集与输入)时,我建议使用更新颖的方法,如数据库支持的Bloom Filters。 这些可以实现为Java对象,并由Drools引用以进行事实查找(但是,在那里进行自定义编码)。

由于Bloom Filters是微小的内存结构,只有基本的insert()/ contains()函数,它们确实有一个缺点……大约1%的误报率。 所以这将作为主缓存。 如果构建Drools问题通常是“NO”作为答案,Bloom Filter支持的事实表构造查找将是闪电般快速并且具有微小的内存占用(在我的实现中每个记录大约1.1个字节)所以1 MB的RAM用于这个案例。 然后在“包含”的情况下(可能是误报),使用数据库支持的事实表来澄清。 同样,如果在80%的情况下,查找都是错误的,那么Bloom Filter将大大节省内存和时间。 否则,纯(任何东西 – Drools事实,数据库等)1M记录查找每次都会非常昂贵(在内存和速度上)。

我担心需要将规则数量作为学生数量的函数 – 这可能会让事情变得棘手(这听起来像是最大的问题)。