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

Posted

技术标签:

【中文标题】在这种情况下我应该使用 Drools 吗?【英文标题】:Should I use Drools in this situation? 【发布时间】:2011-01-17 12:21:12 【问题描述】:

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

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

几个假设:图书馆将处理数百万本书;我不希望书本规则太复杂,平均每个规则最多3个简单的字段约束;系统需要处理的学生数量在100K左右,所以负载比较重。我的问题是:如果加载一百万本书规则,Drools 将占用多少内存?所有这百万条规则的触发速度有多快?如果 Drools 是合适的选择,我想听听经验丰富的用户在设计这样一个系统时的一些最佳实践。谢谢。

【问题讨论】:

既然您的规则约束实际上将位于数据库中 - 为什么不简单地使用 SQL 查询来确定是否允许用户获取这些书籍。 【参考方案1】:

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

通过规则引擎运行大量书籍会很昂贵。特别是因为您不会向用户显示所有结果:每页只有 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 来确保只触发一条规则,并使用salience 来确保它们按照我希望它们的顺序被触发。

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

【讨论】:

+1 重新定义问题 - 我同意您不需要一百万本书作为规则会话事实,只需要学生在“结帐”途中放入“篮子”的那些 【参考方案2】:

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

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

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

类型变得有点复杂,因为实际上一本书可能有两种类型(如果您正在学习某门课程,或者通常如果您是该部门的一员,则允许),但方法应该仍然持有。

【讨论】:

【参考方案3】:

我的问题是:会有多少内存 如果装满一百万,就会流口水 书籍规则?会有多快 所有这些百万规则都可以触发?

你的电脑有多快,你有多少内存?从某种意义上说,您只能通过构建概念证明并用正确数量的(随机生成的)测试数据填充它来找出答案。我的经验是,Drools 比您预期的要快,而且您必须非常了解引擎盖下的内容,才能预测什么会使其变慢。

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

很遗憾,我们没有人对包含一百万个事实的特定设置有答案。

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

【讨论】:

我更喜欢您的解决方案,而不是更改@Michael Deardeuff 建议的问题陈述。当用户导航到最后一页时,您的解决方案将如何显示。您是否期望所有数百万个事实都进入工作记忆?【参考方案4】:

任何时候我们正在查看大型数据集(这个问题是关于...... Drools 是否适合大型数据集案例),请跳出框框思考(如下)。每当我们谈论“数百万个对象”或类似的 log-N 类型问题时,我认为它们所讨论的工具不一定是问题所在。所以是的,可以使用 Drools(或 JBoss 规则),但仅在特定上下文中才有意义...

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

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

【讨论】:

【参考方案5】:

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

【讨论】:

以上是关于在这种情况下我应该使用 Drools 吗?的主要内容,如果未能解决你的问题,请参考以下文章

在这种情况下我应该使用左连接吗?

在这种情况下我应该标准化我的数据库吗? [关闭]

在这种情况下我应该使用 delete 还是 delete[] ? [复制]

在这种情况下我应该使用啥正确的数据类型?

Angular 指令:在这种情况下我应该使用结构还是属性?

在这种情况下我应该使用顶点着色器吗?