这部分问题表明您(或更可能是您的管理层)根本不了解基本的 Drools 生命周期:
推理 - 我们有一个应用程序执行各种规则集。应用程序使所有数据可用,但每个规则集只需要数据的某个子集。有一些监控需求,我们希望准确了解给定规则集访问了哪些数据(在事实对象上调用 getter)。
以下是对其工作原理的非常简化的说明。更详细的解释会超出 StackOverflow 上答案字段的字符数限制。
当您在 Drools 中调用 fireAllRules 时,规则引擎会进入一个称为“匹配”的阶段,即决定实际运行哪些规则。首先,规则引擎根据您的特定规则集(通过显着性、自然顺序等)对规则进行排序,然后遍历此规则列表并执行左侧(LHS;AKA“when 子句”或“条件”)仅用于确定规则是否有效。在每个规则的 LHS 上,每个语句都按顺序执行,直到任何单个语句的计算结果都不为真。
Drools 检查完所有规则的 LHS 后,进入“执行”阶段。在这一点上,它决定触发的所有规则都被执行。使用匹配阶段的相同顺序,遍历每个规则,然后执行规则右侧的语句。
当你考虑到 Drools 支持继承时,事情变得更加复杂,所以规则 B 可以扩展规则 A,所以规则 A 的 LHS 中的语句将在匹配阶段执行两次——一次是在引擎评估它是否可以触发规则 B,并且在引擎评估是否可以触发规则 A 时也会触发一次。
这进一步因为您可以通过使用右侧的特定关键字(RHS;又名“then 子句”或“consequences ") 规则——特别是通过调用update、insert、modify、retract/delete 等。其中一些关键字如insert 将重新评估规则的子集,而其他关键字like update 将在第二个匹配阶段重新评估所有单词。
我在本次讨论中专注于 LHS,因为您的声明说:
有一些监控需求,我们希望准确了解访问了哪些数据(在事实对象上调用 getter)......
除非您有一些非常不标准的规则,否则您的大多数 getter 都应该在您的 LHS 上。这是您应该获取数据并进行比较/检查/决定是否应该触发您的规则的地方。
希望了解触发了哪些“get”调用的请求并不真正有意义——因为在匹配阶段我们将触发大量“get”调用,并且然后忽略结果,因为 LHS 的其他部分不评估为真。
我确实考虑过我们可能在此处遇到了通信问题,而实际需要知道哪些数据实际用于执行 (RHS)。在这种情况下,您应该按照我在 cmets 中的建议使用监听器。如果您编写一个挂钩到 Drools 生命周期的侦听器,特别是到执行阶段(AgendaEventListener's afterMatchFired)。此时,您知道规则匹配并实际执行,因此您可以记录或记录规则名称和详细信息。由于您知道每条规则所需和使用的确切数据,因此您可以跟踪实际使用的数据。
说了这么多,根据我之前的经验,我发现了这部分:
应用程序提供所有数据,但每个规则集只需要数据的某个子集。
我工作的公司采用了这种方法——我们通过将 everything 添加到工作记忆中,使所有数据都可用于所有规则。这个想法是,如果所有数据都可用,那么我们将能够在不更改支持代码的情况下编写规则,因为您将来可能需要的任何数据都已经在工作内存中可用。
当我们有少量数据时,这证明是可以的,但随着公司和产品的增长,这些数据也在增长,我们的规则开始需要大量内存来支持工作内存(尤其是当我们的通话量增加时,因为我们需要为每个规则请求分配更大的堆。)我们使用性能极差的对象传递到工作内存——即 HashMap 和扩展 HashMap 的对象,这一事实加剧了这种情况。
鉴于此,您应该强烈考虑重新考虑您的策略。一旦我们修剪了我们传递到规则中的数据,减少了数据量并将结构更改为高性能 POJO,我们不仅看到资源使用量(主要是堆)大大减少,而且我们还看到了更大的性能改进规则吞吐量,因为规则引擎不需要继续处理和评估工作内存中那些海量且低效的数据量。
最后,关于你在工作记忆中模拟对象的问题——我强烈警告不要尝试这样做。模拟库真的不应该在生产代码中使用。大多数模拟通过利用反射和字节码操作的组合来工作。工作内存中的数据不能保证保持在它传入的初始状态——它在过程中的不同点被序列化和反序列化,所以根据你的特定模拟库的实现方式,你可能会失去“访问权限” " 到特定的模拟实例,相反,您的规则将针对来自该序列化/反序列化过程的功能等效副本。
虽然我从未在这种情况下尝试过,但如果您真的想检测您的 getter 方法,您可以使用方面。不过,您在那里遇到同样问题的可能性是不可忽略的。