在 Cloud Foundry 上使用 spring-data-redis 时检测到资源泄漏

Resource leak detected when using spring-data-redis on cloud foundry

我们开发了一个spring-boot服务,它提供了一个休息api(spring-webflux)并通过RabbitMQ(spring-rabbit)发送数据。该服务部署在 cloud foundry 上,我们在 2.1.4 版本中使用 spring-boot。我们添加了 spring-boot-starter-data-redis 以使用 redis 缓存一些数据,我们得到了以下错误:

[io.netty.util.ResourceLeakDetector] [] LEAK: HashedWheelTimer.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records: 
Created at:
    io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:284)
    io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:217)
    io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:196)
    io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:178)
    io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:162)
    io.lettuce.core.resource.DefaultClientResources.<init>(DefaultClientResources.java:169)
    io.lettuce.core.resource.DefaultClientResources$Builder.build(DefaultClientResources.java:532)
    io.lettuce.core.resource.DefaultClientResources.create(DefaultClientResources.java:233)
    io.lettuce.core.AbstractRedisClient.<init>(AbstractRedisClient.java:98)
    io.lettuce.core.RedisClient.<init>(RedisClient.java:87)
    io.lettuce.core.RedisClient.create(RedisClient.java:124)
    org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.lambda$createClient(LettuceConnectionFactory.java:971)
    java.base/java.util.Optional.orElseGet(Unknown Source)
    org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.createClient(LettuceConnectionFactory.java:971)
    org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.afterPropertiesSet(LettuceConnectionFactory.java:273)
    org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator.create(RedisConnectionFactoryCreator.java:88)
    org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator.create(RedisConnectionFactoryCreator.java:31)
    org.springframework.cloud.Cloud.getServiceConnector(Cloud.java:288)
    org.springframework.cloud.Cloud.getSingletonServiceConnector(Cloud.java:202)
    org.springframework.cloud.config.java.CloudServiceConnectionFactory.redisConnectionFactory(CloudServiceConnectionFactory.java:260)
    org.springframework.cloud.config.java.CloudServiceConnectionFactory.redisConnectionFactory(CloudServiceConnectionFactory.java:242)
    ...

只有当我们 运行 Cloud Foundry 上的服务时才会发生此错误,如果我们 运行 在本地,我们不会收到任何错误。

我们不对连接工厂或我们这边的 stringRedisTemplate 进行任何配置,只使用由 spring-autoconfiguration 配置的 stringRedisTemplate。 我们对 cloud foundry 上的 redis 使用以下配置:

@Configuration
@Profile( "cloud" )
public class CloudSpecificConfig extends AbstractCloudConfig {

   @Bean
   public RedisConnectionFactory redisConnectionFactory() {
      return connectionFactory().redisConnectionFactory();
   }
}

这就是我们使用模板的方式

@Component
@RequiredArgsConstructor
public final class RequestUtil {

   private final StringRedisTemplate myRedisTemplate;

   public String cacheId(String id, String value) {
      myRedisTemplate.opsForValue().set( id, value );

   }
}

这些是我们的 spring 依赖项:

<properties>
  <spring-boot-version>2.1.4.RELEASE</spring-boot-version>
</properties>

  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-webflux</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-configuration-processor</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>
  <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-sleuth</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-cloud-connectors</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-amqp</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
     <version>${spring-boot-version}</version>
  </dependency>

我们这边很困惑,因为我们这边没有做任何具体的配置。它看起来像云上的 spring 配置有问题。我们做错了什么吗?我们需要配置不同的东西吗?这是一个错误吗?

这是我必须做的

我看看能不能找到更优雅的方式

@Bean(destroyMethod = "shutdown")
public DefaultClientResources lettuceClientResources() {
    return DefaultClientResources.create();
}

@SuppressWarnings("unused")
@Bean
public RedisConnectionFactory redisConnectionFactory(DefaultClientResources dependency) {
    return connectionFactory().redisConnectionFactory("redis-pcf-service");
}

最后这个问题在我们这边消失了,因为我们把redis客户端从lettuce换成了jedis。

我们在使用 lettuce 时遇到了问题,我们会失去与云基础设施上的 redis 服务的连接。但是由于在我们更换客户端的同时更新了redis服务,所以我们真的不知道它是否与生菜有关。

也许我们的云架构上基于cloudfoundry的redis服务的自动配置也有问题