使用RabbitMQ使本地内存缓存失效的陷阱

14

我有一个Java Web服务器,目前使用Guava库处理我的内存缓存,并且我经常使用它。现在我需要扩展到多个服务器(2+)以进行故障转移和负载均衡。在此过程中,我从进程内缓存切换到了Memcache(外部服务)。但是,现在几乎每次调用都要进行一次外部调用到另一台服务器,这比内存缓存慢得多,让我不是特别满意。

我考虑不从Memcache中获取数据,而是在每个服务器上继续使用本地缓存,并使用RabbitMQ通知其他服务器何时更新其缓存。因此,如果一个服务器对底层数据进行更改,它将向所有其他服务器广播消息,告诉它们它们的缓存现在无效了。每个服务器都在广播和监听缓存失效消息。

有人知道这种方法可能存在什么问题吗?我有点紧张,因为我找不到其他在生产环境中使用此方法的人。我看到唯一的问题是每个服务器需要更多的内存(内存缓存),并且任何给定服务器获取更新的数据可能需要更长时间。还有其他注意事项吗?


我在DZone上找到了这篇文章,它对我很有帮助,但没有涉及到通过消息传递与其他系统保持缓存一致性的问题。 - CrazyDevMan
请详细说明您的设置。如果您看到250-350毫秒的响应时间,则存在缓存/服务器配置问题。 - theMayer
我们的系统中有类似的设置,并且它是有效的。唯一的区别是,我们使用通知来通知需要清除条目(为了简单起见)。在这种情况下,被通知的实例需要重新查询持久存储以获取最新值,然后更新本地内存缓存。但这完全取决于访问已删除项的频率。 - Grandys
2个回答

8
我对你的问题有点困惑,所以我会用我的理解重新陈述,然后回答我的问题版本。如果我没有符合你的想法,请随便评论。
您有一个Web应用程序,使用进程本地内存缓存数据。您想要扩展到多个节点并保持此程序的相同结构,而不是依赖于具有内置缓存复制的第三方工具(memcached,Couchbase,Redis)。因此,您正在考虑使用RabbitMQ来发布更改以将其发送到各个节点,以便它们可以相应地更新本地缓存。
我最初的反应是,最好使用上述工具之一进行滚动。除了明显的开发和严格的测试之外,Couchbase,Memcached和Redis都旨在解决您所拥有的问题。
此外,在理论上,当您水平扩展时,您的应用程序节点将耗尽可用内存,然后您真的会出现混乱。一旦您达到此限制使您的应用程序不可行的点,您最终将使用其中一种工具,在这一点上,您设计自定义解决方案的所有努力都将归为零。
我能想到的唯一例外是,如果您的应用程序需要大量计算,并且不使用太多内存。在这种情况下,我认为基于RabbitMQ的解决方案很容易,但是您需要制定某种程序,以便定期在服务器之间同步缓存,如果RMQ中错过了消息,那么应该处理节点启动和关闭的方式。
编辑
考虑到评论中的声明,您看到的访问时间为几百毫秒,我将建议您首先检查设置。从Memcached(或Couchbase或Redis等)实例中缓存单个项的典型读取时间为次毫秒级别(如果我记得正确,则大约为0.1毫秒),因此,您的“问题child”缓存服务器的性能与应该相差数个数量级。从那里开始,然后看看您是否仍然有同样的问题。

听起来很有道理,但并不完全符合我的问题。我的原始缓存是进程本地的(将Guava缓存视为映射或字典)。它没有任何内置机制来扩展到多个节点。Memcache具有此功能,这就是我切换到它的原因,但现在缓存不再是我的Web服务器本地的,而是在另一台机器上,这会减慢速度。 - CrazyDevMan
4
你是否有什么原因不能在网页服务器上运行memcached? - Chris Johnson
一些memcached客户端保持与服务器的连接以优化响应时间; 我对于你的使用情况感到困惑,即少数几个十分之一毫秒足以导致性能明显下降。你能否详细说明一下? - theMayer
@ChrisJohnson,我非常喜欢你的建议,在同一台服务器上运行memcached,我会尝试一下。 - CrazyDevMan
@rmayer06,每个对memcache的调用大约需要150到200毫秒(几乎所有时间都花在网络请求上)。由于我现在只是将模型存储在缓存中,因此有些请求需要进行2次调用(序列化),因为我需要第一个模型来确定第二次调用要获取哪个模型。 - CrazyDevMan
显示剩余3条评论

1
我们正在使用类似的东西来处理只读数据,不需要每次更新。我怀疑这对你来说是否是个好计划。想象一下,你应该在每个实例上有一个额外的服务,它将监视队列并将更改处理到内存存储中。这很难测试。
你确定大部分时间都花在服务器之间的通信上吗?也许你运行了多个调用?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接