负载平衡应用程序中的缓存失效
Cache invalidation in load balanced application
我正在开发的应用程序使用简单的 HashMap 作为来自数据库的某些对象的缓存。这远非理想,但这些缓存列表的数据量确实很小(少于 100 个)并且不会经常更改。此解决方案提供最小的开销。当其中一个缓存列表中的项目发生更改时,其值将在 HashMap 中被替换。
我们即将发布此应用程序的生产日期。为了提供一个可合理扩展的解决方案,我们提供了一个负载平衡解决方案。平衡器在几个 Wildfly 节点之间切换,每个节点都包含整个应用程序,数据库除外。
现在的问题是,当缓存项发生更改时,它只会在其中一个节点中更新。更改不会应用于其他节点中的缓存。可能的解决方案是:
- 禁用缓存。不是一个选项。
- 使用像Ehcache Server 这样的缓存服务器。这样一来,所有节点都会有一个缓存。然而,由于 REST 调用,这个问题会产生过多的开销。
- 每个节点中的附加 Web 服务。此 Web 服务将跟踪所有负载平衡的节点。当一个节点中的缓存值发生变化时,该节点将向其他节点发出信号以驱逐其缓存。
- 像 Ehcache 这样具有信令功能的现成解决方案。这个存在吗?
我的问题是:是否有提供最后解决方案的产品(免费且具有开放许可证,可商用)?如果没有,我会实施第三种解决方案。有什么 risks/mistakes 我需要注意的吗?
Risks/mistakes:当然重要的一点是数据的一致性。从数据库缓存数据时,我通常会确保在更新时使用事务。通常我使用这样的模式:
begin transaction
invalidate cache entries in the transaction
update database
commit transaction
如果在更新期间发生缓存未命中,读取需要等到事务提交。
对于您的用例,典型的选择是集群或分布式缓存,例如:HazelCast、Infinispan、Apache Ignite。但是,不知何故,这在您的用例中确实很重。
另一种方法是实施自己的机制来向所有节点发布失效事件。这仍然不是一件容易的事,因为您可能希望确保每个节点都收到消息,但如果一个节点同时出现故障,也需要容错。所以你可能想为此使用一个合适的库,例如JGroups 或各种 MQ 产品。
我在没有使用 JGroups 或其他信号库的情况下实现了它。每个节点都有一个 REST 端点来清除缓存。当一个节点启动时,它会使用它的 IP、域和令牌在 DB table 中注册自己。当它关闭时,它会删除它的记录。
当一个对象在一个节点中更新时,该节点会清除其缓存并启动多个线程,这些线程使用 Unirest 向所有其他节点发送 REST 调用(带有其令牌和对象类型),这些节点依次检查令牌并驱逐他们的缓存。抛出错误时,调用的节点从列表中删除。
在安全性和容错性方面应该有所改进。节点的移除现在真的很悲观。只有在多次尝试失败后,才应删除该节点。现在,这个简单的解决方案就可以了!
我正在开发的应用程序使用简单的 HashMap 作为来自数据库的某些对象的缓存。这远非理想,但这些缓存列表的数据量确实很小(少于 100 个)并且不会经常更改。此解决方案提供最小的开销。当其中一个缓存列表中的项目发生更改时,其值将在 HashMap 中被替换。
我们即将发布此应用程序的生产日期。为了提供一个可合理扩展的解决方案,我们提供了一个负载平衡解决方案。平衡器在几个 Wildfly 节点之间切换,每个节点都包含整个应用程序,数据库除外。
现在的问题是,当缓存项发生更改时,它只会在其中一个节点中更新。更改不会应用于其他节点中的缓存。可能的解决方案是:
- 禁用缓存。不是一个选项。
- 使用像Ehcache Server 这样的缓存服务器。这样一来,所有节点都会有一个缓存。然而,由于 REST 调用,这个问题会产生过多的开销。
- 每个节点中的附加 Web 服务。此 Web 服务将跟踪所有负载平衡的节点。当一个节点中的缓存值发生变化时,该节点将向其他节点发出信号以驱逐其缓存。
- 像 Ehcache 这样具有信令功能的现成解决方案。这个存在吗?
我的问题是:是否有提供最后解决方案的产品(免费且具有开放许可证,可商用)?如果没有,我会实施第三种解决方案。有什么 risks/mistakes 我需要注意的吗?
Risks/mistakes:当然重要的一点是数据的一致性。从数据库缓存数据时,我通常会确保在更新时使用事务。通常我使用这样的模式:
begin transaction
invalidate cache entries in the transaction
update database
commit transaction
如果在更新期间发生缓存未命中,读取需要等到事务提交。
对于您的用例,典型的选择是集群或分布式缓存,例如:HazelCast、Infinispan、Apache Ignite。但是,不知何故,这在您的用例中确实很重。
另一种方法是实施自己的机制来向所有节点发布失效事件。这仍然不是一件容易的事,因为您可能希望确保每个节点都收到消息,但如果一个节点同时出现故障,也需要容错。所以你可能想为此使用一个合适的库,例如JGroups 或各种 MQ 产品。
我在没有使用 JGroups 或其他信号库的情况下实现了它。每个节点都有一个 REST 端点来清除缓存。当一个节点启动时,它会使用它的 IP、域和令牌在 DB table 中注册自己。当它关闭时,它会删除它的记录。
当一个对象在一个节点中更新时,该节点会清除其缓存并启动多个线程,这些线程使用 Unirest 向所有其他节点发送 REST 调用(带有其令牌和对象类型),这些节点依次检查令牌并驱逐他们的缓存。抛出错误时,调用的节点从列表中删除。
在安全性和容错性方面应该有所改进。节点的移除现在真的很悲观。只有在多次尝试失败后,才应删除该节点。现在,这个简单的解决方案就可以了!