【问题标题】:Looking for way of inspecting Drools interactions with Facts寻找检查 Drools 与 Facts 交互的方法
【发布时间】:2021-07-21 17:00:26
【问题描述】:

我正在将类似 pojo 的对象/事实插入 KieSession 并具有与这些对象交互的规则。在触发所有规则之后,我希望能够检查规则访问了哪些对象及其方法。从概念上讲,这类似于模拟。

我尝试使用 Mockito 并将模拟对象插入 kieSession。我能够得到一个被调用的方法列表,但似乎并不是所有的交互都出现了。 不确定这是 Mockito 的限制,还是 Drools 管理事实和生命周期的方式打破了模拟。

也许有更好的方法来实现这一点?

更新:推理 - 我们有一个应用程序执行各种规则集。应用程序使所有数据可用,但每个规则集只需要数据的某个子集。有一些监控需求,我们希望准确查看给定规则集访问了哪些数据(在事实对象上调用 getter)。

【问题讨论】:

  • 为什么不使用监听器?尝试手动模拟对象会因序列化/反序列化而变得复杂,而且您必须真正了解 drools 生命周期以及不同阶段(例如匹配)如何工作。
  • 如果除了描述您的尝试之外,您还要解释为什么您要尝试这样做,这将有所帮助。您是在尝试近似代码覆盖率还是单元测试之外的东西?您是否试图找出规则命中或生产代码之外的东西?等
  • @RoddyoftheFrozenPeas 我们有一个应用程序执行各种规则集。应用程序提供所有数据,但每个规则集只需要数据的某个子集。有一些监控需求,我们希望准确了解给定规则集使用了哪些数据。希望这会有所帮助。
  • @RoddyoftheFrozenPeas 查看文档我没有看到可以提供对象访问信息的事件类型,还是我遗漏了什么?
  • 使用提供的接口创建一个监听器。它可以像跟踪规则命中一样简单——如果你知道命中了什么规则(例如执行),你就知道你使用什么数据,因为你有你的规则定义,因此有一个规则标识符 -> 数据的映射。

标签: mockito drools


【解决方案1】:

这部分问题表明您(或更可能是您的管理层)根本不了解基本的 Drools 生命周期:

推理 - 我们有一个应用程序执行各种规则集。应用程序使所有数据可用,但每个规则集只需要数据的某个子集。有一些监控需求,我们希望准确了解给定规则集访问了哪些数据(在事实对象上调用 getter)。

以下是对其工作原理的非常简化的说明。更详细的解释会超出 StackOverflow 上答案字段的字符数限制。

当您在 Drools 中调用 fireAllRules 时,规则引擎会进入一个称为“匹配”的阶段,即决定实际运行哪些规则。首先,规则引擎根据您的特定规则集(通过显着性、自然顺序等)对规则进行排序,然后遍历此规则列表并执行左侧(LHS;AKA“when 子句”或“条件”)仅用于确定规则是否有效。在每个规则的 LHS 上,每个语句都按顺序执行,直到任何单个语句的计算结果都不为真。

Drools 检查完所有规则的 LHS 后,进入“执行”阶段。在这一点上,它决定触发的所有规则都被执行。使用匹配阶段的相同顺序,遍历每个规则,然后执行规则右侧的语句。

当你考虑到 Drools 支持继承时,事情变得更加复杂,所以规则 B 可以扩展规则 A,所以规则 A 的 LHS 中的语句将在匹配阶段执行两次——一次是在引擎评估它是否可以触发规则 B,并且在引擎评估是否可以触发规则 A 时也会触发一次。

进一步因为您可以通过使用右侧的特定关键字(RHS;又名“then 子句”或“consequences ") 规则——特别是通过调用updateinsertmodifyretract/delete 等。其中一些关键字如insert 将重新评估规则的子集,而其他关键字like update 将在第二个匹配阶段重新评估所有单词。

我在本次讨论中专注于 LHS,因为您的声明说:

有一些监控需求,我们希望准确了解访问了哪些数据(在事实对象上调用 getter)......

除非您有一些非常不标准的规则,否则您的大多数 getter 都应该在您的 LHS 上。这是您应该获取数据并进行比较/检查/决定是否应该触发您的规则的地方。

希望了解触发了哪些“get”调用的请求并不真正有意义——因为在匹配阶段我们将触发大量“get”调用,并且然后忽略结果,因为 LHS 的其他部分不评估为真。


我确实考虑过我们可能在此处遇到了通信问题,而实际需要知道哪些数据实际用于执行 (RHS)。在这种情况下,您应该按照我在 cmets 中的建议使用监听器。如果您编写一个挂钩到 Drools 生命周期的侦听器,特别是到执行阶段(AgendaEventListener's afterMatchFired)。此时,您知道规则匹配并实际执行,因此您可以记录或记录规则名称和详细信息。由于您知道每条规则所需和使用的确切数据,因此您可以跟踪实际使用的数据。


说了这么多,根据我之前的经验,我发现了这部分:

应用程序提供所有数据,但每个规则集只需要数据的某个子集。

我工作的公司采用了这种方法——我们通过将 everything 添加到工作记忆中,使所有数据都可用于所有规则。这个想法是,如果所有数据都可用,那么我们将能够在不更改支持代码的情况下编写规则,因为您将来可能需要的任何数据都已经在工作内存中可用。

当我们有少量数据时,这证明是可以的,但随着公司和产品的增长,这些数据也在增长,我们的规则开始需要大量内存来支持工作内存(尤其是当我们的通话量增加时,因为我们需要为每个规则请求分配更大的堆。)我们使用性能极差的对象传递到工作内存——即 HashMap 和扩展 HashMap 的对象,这一事实加剧了这种情况。

鉴于此,您应该强烈考虑重新考虑您的策略。一旦我们修剪了我们传递到规则中的数据,减少了数据量并将结构更改为高性能 POJO,我们不仅看到资源使用量(主要是堆)大大减少,而且我们还看到了更大的性能改进规则吞吐量,因为规则引擎不需要继续处理和评估工作内存中那些海量且低效的数据量。


最后,关于你在工作记忆中模拟对象的问题——我强烈警告不要尝试这样做。模拟库真的不应该在生产代码中使用。大多数模拟通过利用反射和字节码操作的组合来工作。工作内存中的数据不能保证保持在它传入的初始状态——它在过程中的不同点被序列化和反序列化,所以根据你的特定模拟库的实现方式,你可能会失去“访问权限” " 到特定的模拟实例,相反,您的规则将针对来自该序列化/反序列化过程的功能等效副本。

虽然我从未在这种情况下尝试过,但如果您真的想检测您的 getter 方法,您可以使用方面。不过,您在那里遇到同样问题的可能性是不可忽略的。

【讨论】:

  • 感谢您为撰写本文所付出的努力!这非常有见地,对我有很大帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多