我们使用的是 spring-data-redis
随着 spring-cache
抽象和 lettuce
作为我们的redis-client.此外,我们在一些方法上使用多线程和异步执行。
一个工作流的例子是这样的。
主方法A (main-thread) --> 调用方法B (@Async
),这是一个代理方法,能够在另一个线程中异步运行逻辑。--> 方法B调用方法C,而方法C则是 @Cacheable
. 该 @Cacheable
Annotation处理对我们redis缓存的读写。
有什么问题?
Lettuce
是 Netty
-基于的,它的工作原理是依靠 DirectMemory
. 由于 @Async
程序的性质,我们有多个线程,使用了 LettuceConnection
(因此 Netty
)同时进行。
根据设计,所有线程将使用相同的(? Netty
它共享 DirectMemory
. 由于显然太小 MaxDirectMemorySize
我们得到 OutOfDirectMemoryError
当有太多线程在访问 Netty
.
例子: 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.orgbuildpacksjava 并计算出 MaxDirectMemorySize
使用 https:/github.comcloudfoundryjava-buildpack-memory-calculator。.
这就导致了 MaxDirectMemorySize=10M
. 实际可用内存为4GB,则计算出的内存容量为4GB。MaxDirectMemorySize
可能是太保守了。这可能是问题的一部分。
解决这个问题的可能办法
MaxDirectMemorySize
但我们不确定这是否足够。Netty
不使用 DirectMemory
(noPreferDirect=true
) --> Netty
就会使用堆,但是我们不安全,如果这样会使我们的应用速度太慢,如果 Netty
贪多嚼不烂Lettuce
到 shareNativeConnection=false
--> 这将导致与redis的多个连接。我们的问题是:我们如何用正确的方法解决这个问题?
我很乐意提供更多关于我们如何设置应用程序的配置(application.yml,LettuceConnection等)的信息,如果这些信息有助于解决这个问题。
感谢大家的支持。https:/gitter.imlettuce-ioLobby。 我们得到了一些关于如何处理这些问题的线索。
正如怀疑的那样,10M MaxDirectMemorySize
考虑到总的可用内存,这个值过于保守。
建议增加这个值。由于我们实际上并不知道有多少内存 Netty
会需要更稳定的表现,我们想到了以下步骤。
首先,我们将禁用 我们将禁用 Netty
的偏好。MaxDirectMemory
通过设置 noPreferDirect=true
. Netty
就会使用堆缓冲区。
其次,我们将监控堆内存的使用量。 我们将监控有多少堆内存。Netty
在运行过程中会消耗多少内存。这样,我们就可以推断出一个平均的内存消耗量。Netty
.
第三,我们将取平均内存消耗值,并将其设为 "新"。 我们将采取平均内存消耗值 并将其设置为 "新" MaxDirectMemorySize
通过在JVM选项中设置 -XX:MaxDirectMemorySize
. 然后我们将重新启用 Netty
来使用 DirectMemory
通过设置 noPreferDirect=false
.
第四,监控日志和异常情况。 监视日志和异常情况,看看我们是否还有问题 或者说这是否成功了。