Drools - 存储多状态会话
Drools - Store Multi Stateful Sessions
我们在我们的平台中实施了 drools 引擎,以便能够评估来自流的规则。
在我们的用例中,我们有一个变化检测流,其中包含多个实体的变化。
需要在一段时间内针对流中的每个实体评估规则,并使其状态与其他实体(会话)区分开来。这些规则根据每个实体的状态生成警报。由于这个原因,实体应该进入边界,因此一个实体的状态不会干扰其他实体。
为实现这一点,我们为每个实体 ID 创建一个会话作为 Spring Bean,并将其存储在 inMemory HashMap 中。因此,每次实体到达时,我们都会尝试使用它的 Id 在 inMemory Map 上找到它的会话。如果我们得到一个 null return 我们就创建它。
这似乎不是完成它的正确方法。因为它不提供灾难恢复策略,也不提供出色的内存管理。
我们可以使用某种内存数据库,例如 Redis 或 Memchached。但我不认为它能够精确地恢复有状态会话。
有人知道如何使用具有多会话的嵌入式 Drools 以正确的方式实现灾难恢复和良好的内存管理吗?该平台是否提供一些解决方案?
非常感谢您的关注与支持
答案不是尝试持久化和重用会话,而是持久化对实体当前状态建模的对象。
您当前的工作流程是这样的:
- 实体到达您的应用程序(来自变更检测流或其他地方)
- 您在哈希图上进行查找以获取存储了实体状态的会话
- 您启动规则,更新会话(可能还有实体)
- 您坚持会话 in-memory。
您的工作流程应该是这样的:
- (相同)实体到达您的应用程序
- 您对实体状态的外部数据源执行 look-up -- 例如来自数据库或数据存储
- 你触发规则,传入实体状态。您没有更新会话,而是更新了状态实例。
- 您将状态保存到外部数据源。
如果添加适当的 write-through 缓存,您可以同时保证性能和一致性。如果您为数据源实施适当的锁定/事务处理,这也将允许您横向扩展应用程序。
这是一个玩具示例。
假设我们有一个模拟图书馆的应用程序,允许用户借阅图书。一个用户一次最多只能借阅3本书。
'event'我们收到模型一本书check-in或check-out事件:
class BookBorrowEvent {
int userId;
int bookId;
EventType eventType; // EventType.CHECK_IN or EventType.CHECK_OUT
}
在外部数据源中,我们维护一条 UserState 记录——可能作为传统 RDBMS 或聚合中的不同记录;我们如何存储它与示例并不相关。但是假设我们从数据源 returned 的 UserState 记录看起来像这样:
class UserState {
int userId;
int[] borrowedBookIds;
}
当我们收到事件时,我们将首先从外部数据存储(或 internally-managed write-through 缓存中检索用户状态,然后将 UserState 添加到规则输入中。当然,我们应该适当地处理我们的会话(使用后处理它们,根据需要使用会话池)。
public void handleBookBorrow(BookBorrowEvent event) {
UserState state = getUserStateFromStore(event.getUserId());
KieSession kieSession = ...;
kieSession.insert( event );
kieSession.insert( state );
kieSession.fireAllRules();
persistUserStateToStore(state);
}
然后您的规则将针对 UserState
实例执行它们的工作,而不是将值存储在局部变量中。
一些示例规则:
rule "User borrows a book"
when
BookBorrowEvent( eventType == EventType.CHECK_OUT,
$bookId: bookId != null )
$state: UserState( $checkedOutBooks: borrowedBookIds not contains $bookId )
Integer( this < 3 ) from $checkedOutBooks.length
then
modify( $state ) { ... }
end
rule "User returns a book"
when
BookBorrowEvent( eventType == EventType.CHECK_IN,
$bookId: bookId != null )
$state: UserState( $checkedOutBooks: borrowedBookIds contains $bookId )
then
modify( $state ) { ... }
end
显然是一个玩具示例,但您可以轻松地为以下情况添加额外的规则,例如用户尝试签出一本书的副本,用户尝试 return 他们没有签出的书, return 如果用户超过 3 本书最大借阅限制,则会出现错误,添加 time-based 允许的结帐长度等逻辑
即使您正在使用 stream-based 处理以便您可以利用时间运算符,此工作流程仍然有效,因为您会在收到状态实例时将其传递到评估流中。当然,在这种情况下,出于性能原因,正确实现 write-through 缓存更为重要(除非您的时态运算符足够宽容以允许某些数据源事务延迟)。您需要做的唯一更改是重新调整您的规则,将它们的数据持久性定位到状态对象而不是会话本身——无论如何通常不推荐这样做,因为会话被设计为被处理掉。
我们在我们的平台中实施了 drools 引擎,以便能够评估来自流的规则。 在我们的用例中,我们有一个变化检测流,其中包含多个实体的变化。
需要在一段时间内针对流中的每个实体评估规则,并使其状态与其他实体(会话)区分开来。这些规则根据每个实体的状态生成警报。由于这个原因,实体应该进入边界,因此一个实体的状态不会干扰其他实体。
为实现这一点,我们为每个实体 ID 创建一个会话作为 Spring Bean,并将其存储在 inMemory HashMap 中。因此,每次实体到达时,我们都会尝试使用它的 Id 在 inMemory Map 上找到它的会话。如果我们得到一个 null return 我们就创建它。
这似乎不是完成它的正确方法。因为它不提供灾难恢复策略,也不提供出色的内存管理。
我们可以使用某种内存数据库,例如 Redis 或 Memchached。但我不认为它能够精确地恢复有状态会话。
有人知道如何使用具有多会话的嵌入式 Drools 以正确的方式实现灾难恢复和良好的内存管理吗?该平台是否提供一些解决方案?
非常感谢您的关注与支持
答案不是尝试持久化和重用会话,而是持久化对实体当前状态建模的对象。
您当前的工作流程是这样的:
- 实体到达您的应用程序(来自变更检测流或其他地方)
- 您在哈希图上进行查找以获取存储了实体状态的会话
- 您启动规则,更新会话(可能还有实体)
- 您坚持会话 in-memory。
您的工作流程应该是这样的:
- (相同)实体到达您的应用程序
- 您对实体状态的外部数据源执行 look-up -- 例如来自数据库或数据存储
- 你触发规则,传入实体状态。您没有更新会话,而是更新了状态实例。
- 您将状态保存到外部数据源。
如果添加适当的 write-through 缓存,您可以同时保证性能和一致性。如果您为数据源实施适当的锁定/事务处理,这也将允许您横向扩展应用程序。
这是一个玩具示例。
假设我们有一个模拟图书馆的应用程序,允许用户借阅图书。一个用户一次最多只能借阅3本书。
'event'我们收到模型一本书check-in或check-out事件:
class BookBorrowEvent {
int userId;
int bookId;
EventType eventType; // EventType.CHECK_IN or EventType.CHECK_OUT
}
在外部数据源中,我们维护一条 UserState 记录——可能作为传统 RDBMS 或聚合中的不同记录;我们如何存储它与示例并不相关。但是假设我们从数据源 returned 的 UserState 记录看起来像这样:
class UserState {
int userId;
int[] borrowedBookIds;
}
当我们收到事件时,我们将首先从外部数据存储(或 internally-managed write-through 缓存中检索用户状态,然后将 UserState 添加到规则输入中。当然,我们应该适当地处理我们的会话(使用后处理它们,根据需要使用会话池)。
public void handleBookBorrow(BookBorrowEvent event) {
UserState state = getUserStateFromStore(event.getUserId());
KieSession kieSession = ...;
kieSession.insert( event );
kieSession.insert( state );
kieSession.fireAllRules();
persistUserStateToStore(state);
}
然后您的规则将针对 UserState
实例执行它们的工作,而不是将值存储在局部变量中。
一些示例规则:
rule "User borrows a book"
when
BookBorrowEvent( eventType == EventType.CHECK_OUT,
$bookId: bookId != null )
$state: UserState( $checkedOutBooks: borrowedBookIds not contains $bookId )
Integer( this < 3 ) from $checkedOutBooks.length
then
modify( $state ) { ... }
end
rule "User returns a book"
when
BookBorrowEvent( eventType == EventType.CHECK_IN,
$bookId: bookId != null )
$state: UserState( $checkedOutBooks: borrowedBookIds contains $bookId )
then
modify( $state ) { ... }
end
显然是一个玩具示例,但您可以轻松地为以下情况添加额外的规则,例如用户尝试签出一本书的副本,用户尝试 return 他们没有签出的书, return 如果用户超过 3 本书最大借阅限制,则会出现错误,添加 time-based 允许的结帐长度等逻辑
即使您正在使用 stream-based 处理以便您可以利用时间运算符,此工作流程仍然有效,因为您会在收到状态实例时将其传递到评估流中。当然,在这种情况下,出于性能原因,正确实现 write-through 缓存更为重要(除非您的时态运算符足够宽容以允许某些数据源事务延迟)。您需要做的唯一更改是重新调整您的规则,将它们的数据持久性定位到状态对象而不是会话本身——无论如何通常不推荐这样做,因为会话被设计为被处理掉。