首页 > 解决方案 > 在多线程上下文中使用带有生菜的 Spring-Data-Redis 的 OutOfDirectMemoryError

问题描述

我们使用spring-data-redis抽象spring-cachelettuce作为我们的 redis 客户端。此外,我们在某些方法上使用多线程和异步执行。

示例工作流程如下所示:

Main-Method A (main-thread) --> 调用 Method B ( @Async),这是一个代理方法,能够在另一个线程中异步运行逻辑。--> 方法 B 调用方法 C,即@Cacheable. Annotation 处理对我们的 redis-cache的@Cacheable读/写。

有什么问题?

Lettuceis Netty-based 依赖于DirectMemory. 由于@Async我们程序的性质,我们有多个线程同时使用LettuceConnection(因此Netty)。

按照设计,所有线程都将使用相同的 (?)Netty共享DirectMemory. 由于 a 显然太小了,当有太多线程访问时,MaxDirectMemorySize我们得到一个.OutOfDirectMemoryErrorNetty

例子:
io.lettuce.core.RedisException: io.netty.handler.codec.EncoderException: io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 8388352 byte(s) of direct memory (used: 4746467, max: 10485760)

到目前为止我们发现了什么?

我们使用https://docs.cloudfoundry.org/buildpacks/java/MaxDirectMemorySize使用https://github.com/cloudfoundry/java-buildpack-memory-calculator计算。

这导致MaxDirectMemorySize=10M. 计算出的实际可用内存为 4GBMaxDirectMemorySize可能是保守的方式。这可能是问题的一部分。

问题的潜在解决方案

我们的问题是:我们如何以正确的方式解决这个问题?

我很乐意提供更多关于我们如何设置应用程序配置的信息(application.yml、LettuceConnection 等),如果其中任何一个有助于解决问题的话。

标签: multithreadingasynchronousredisspring-data-redislettuce

解决方案


感谢那些在:https ://gitter.im/lettuce-io/Lobby我们得到了一些关于如何解决这些问题的线索。

MaxDirectMemorySize正如怀疑的那样,考虑到总可用内存,10M过于保守。

建议是增加这个值。由于我们实际上并不知道需要多少内存Netty才能更稳定地执行,因此我们考虑了以下步骤。

首先:我们将通过设置禁用Netty的首选项。然后将使用堆缓冲区。MaxDirectMemorynoPreferDirect=trueNetty

第二:Netty然后我们将监控在操作期间将消耗多少堆内存。这样做,我们将能够推断出Netty.

第三:我们将取平均内存消耗值并将其设置为“新” ,方法MaxDirectMemorySize是在 JVM 选项中设置它-XX:MaxDirectMemorySize。然后我们将通过设置重新启用Netty使用。DirectMemorynoPreferDirect=false

第四:监控日志条目和异常,看看我们是否仍然有问题,或者这样做是否有效。

[更新] 我们从上述步骤开始,但意识到该设置noPreferDirect=true并不能完全阻止 netty 使用 DirectMemory。对于某些用例(nio-Processes)Netty仍然使用 DirectMemory。

所以我们不得不增加MaxDirectMemorySize.

现在我们设置以下 JAVA_OPTS -Dio.netty.noPreferDirect=true -XX:MaxDirectMemorySize=100M。这可能会解决我们的问题。


推荐阅读