Skip to content

Commit 98707c0

Browse files
author
codeba
committed
fix(redis-keeper-spring-boot-starter): Fixed the issue where idle redis connections were not closed after dynamically refreshing the configuration, and the same configuration reuses the redis client.
1 parent aa6ea5c commit 98707c0

File tree

9 files changed

+281
-30
lines changed

9 files changed

+281
-30
lines changed

redis-keeper-core/src/main/java/org/codeba/redis/keeper/core/CacheTemplate.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,9 @@ public interface CacheTemplate extends KBitSet, KGeneric, KGeo, KMap, KHyperLogL
6565
*/
6666
CompletableFuture<List<?>> pipelineWithResponsesAsync(Consumer<KBatch> batchConsumer);
6767

68+
/**
69+
* Destroy.
70+
*/
71+
void destroy();
72+
6873
}

redis-keeper-example/redis-keeper-example-springcloud/src/main/java/org/codeba/redis/keeper/springcloud/TestController.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717
package org.codeba.redis.keeper.springcloud;
1818

1919
import lombok.RequiredArgsConstructor;
20+
import lombok.extern.slf4j.Slf4j;
2021
import org.codeba.redis.keeper.core.CacheTemplate;
2122
import org.codeba.redis.keeper.core.CacheTemplateProvider;
2223
import org.springframework.web.bind.annotation.RequestMapping;
2324
import org.springframework.web.bind.annotation.RestController;
2425

2526
import java.util.Optional;
27+
import java.util.concurrent.CompletableFuture;
2628

2729
/**
2830
* The type Test controller.
2931
*
3032
* @author codeba
3133
*/
34+
@Slf4j
3235
@RestController
3336
@RequiredArgsConstructor
3437
public class TestController {
@@ -42,12 +45,38 @@ public class TestController {
4245
* Test refresh.
4346
*/
4447
@RequestMapping("/refresh")
45-
public void testRefresh() {
46-
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds1");
47-
templateOptional.ifPresent(cacheTemplate -> {
48-
cacheTemplate.set("foo", "bar");
49-
cacheTemplate.del("foo");
48+
public boolean testRefresh() {
49+
final long start = System.currentTimeMillis();
50+
51+
final CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {
52+
for (int i = 0; i < 100000; i++) {
53+
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds4");
54+
templateOptional.ifPresent(cacheTemplate -> {
55+
cacheTemplate.incr("testRefresh");
56+
});
57+
}
58+
});
59+
final CompletableFuture<Void> f2 = CompletableFuture.runAsync(() -> {
60+
for (int i = 0; i < 100000; i++) {
61+
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds4");
62+
templateOptional.ifPresent(cacheTemplate -> {
63+
cacheTemplate.incr("testRefresh");
64+
});
65+
}
66+
});
67+
final CompletableFuture<Void> f3 = CompletableFuture.runAsync(() -> {
68+
for (int i = 0; i < 100000; i++) {
69+
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds4");
70+
templateOptional.ifPresent(cacheTemplate -> {
71+
cacheTemplate.incr("testRefresh");
72+
});
73+
}
5074
});
75+
76+
CompletableFuture.allOf(f1, f2, f3).join();
77+
log.info("cost time: {}", (System.currentTimeMillis() - start));
78+
79+
return true;
5180
}
5281

5382

redis-keeper-spring-boot-starter/src/main/java/org/codeba/redis/keeper/spring/boot/RedisDatasourceProperties.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.codeba.redis.keeper.spring.boot;
1818

1919
import lombok.Data;
20+
import org.springframework.beans.factory.DisposableBean;
2021
import org.springframework.boot.context.properties.ConfigurationProperties;
2122

2223
import java.util.List;
@@ -29,8 +30,12 @@
2930
*/
3031
@Data
3132
@ConfigurationProperties(prefix = "redis-keeper.redis")
32-
public class RedisDatasourceProperties {
33+
public class RedisDatasourceProperties implements DisposableBean {
3334

35+
/**
36+
* The Lazy refresh.
37+
*/
38+
private boolean lazyRefresh = true;
3439
/**
3540
* The Datasource.
3641
*/
@@ -41,4 +46,14 @@ public class RedisDatasourceProperties {
4146
*/
4247
private Map<String, List<RedisKeeperProperties>> datasources;
4348

49+
/**
50+
* Destroy.
51+
*/
52+
@Override
53+
public void destroy() {
54+
lazyRefresh = true;
55+
datasource = null;
56+
datasources = null;
57+
}
58+
4459
}

redis-keeper-spring-boot-starter/src/main/java/org/codeba/redis/keeper/spring/boot/RedisKeeperAutoConfiguration.java

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@
2626
import org.redisson.config.Config;
2727
import org.redisson.config.SentinelServersConfig;
2828
import org.redisson.config.SingleServerConfig;
29+
import org.springframework.aop.scope.ScopedProxyUtils;
2930
import org.springframework.beans.factory.annotation.Autowired;
3031
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
3132
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3233
import org.springframework.cloud.context.config.annotation.RefreshScope;
34+
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
35+
import org.springframework.context.ApplicationContext;
3336
import org.springframework.context.annotation.Bean;
3437
import org.springframework.context.annotation.Configuration;
38+
import org.springframework.context.event.EventListener;
3539
import org.springframework.util.ReflectionUtils;
3640
import org.springframework.util.StringUtils;
3741

@@ -56,30 +60,61 @@
5660
@SuppressWarnings({"unchecked"})
5761
@EnableConfigurationProperties({RedisDatasourceProperties.class, RedissonDatasourceProperties.class})
5862
public class RedisKeeperAutoConfiguration<T> {
63+
/**
64+
* The constant PROVIDER_BEAN_NAME.
65+
*/
66+
private static final String PROVIDER_BEAN_NAME = "cacheTemplateProvider";
67+
68+
/**
69+
* The Context.
70+
*/
71+
@Autowired
72+
private ApplicationContext context;
5973

6074
/**
6175
* The Cache datasource.
6276
*/
6377
@Autowired(required = false)
6478
private CacheDatasource<T> cacheDatasource;
6579

80+
/**
81+
* The Redis properties.
82+
*/
83+
@Autowired
84+
private RedisDatasourceProperties redisProperties;
85+
86+
/**
87+
* The Redisson properties.
88+
*/
89+
@Autowired
90+
private RedissonDatasourceProperties redissonProperties;
91+
6692
/**
6793
* Cache template provider cache template provider.
94+
* <p>
6895
*
69-
* @param redisProperties the redis properties
70-
* @param redissonProperties the redisson properties
7196
* @return the cache template provider
7297
* @throws IOException the io exception
7398
*/
74-
@Bean
99+
@Bean(name = RedisKeeperAutoConfiguration.PROVIDER_BEAN_NAME)
75100
@RefreshScope
76-
public CacheTemplateProvider<T> cacheTemplateProvider(RedisDatasourceProperties redisProperties, RedissonDatasourceProperties redissonProperties) throws IOException {
101+
public CacheTemplateProvider<T> cacheTemplateProvider() throws IOException {
77102
final Map<String, T> loadMap = load(redisProperties, redissonProperties);
78-
final Map<String, List<T>> loadsMap = loads(redisProperties, redissonProperties);
79-
80-
return new CacheTemplateProvider<>(loadMap, loadsMap);
103+
final Map<String, List<T>> loadListMap = loads(redisProperties, redissonProperties);
104+
// clean
105+
cacheDatasource.clean();
106+
return new CacheTemplateProvider<>(loadMap, loadListMap);
81107
}
82108

109+
/**
110+
* On refresh.
111+
*/
112+
@EventListener(RefreshScopeRefreshedEvent.class)
113+
public void onRefresh() {
114+
if (!redisProperties.isLazyRefresh() || !redissonProperties.isLazyRefresh()) {
115+
this.context.getBean(ScopedProxyUtils.getTargetBeanName(PROVIDER_BEAN_NAME));
116+
}
117+
}
83118

84119
/**
85120
* Load map.

redis-keeper-spring-boot-starter/src/main/java/org/codeba/redis/keeper/spring/boot/RedissonDatasourceProperties.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.codeba.redis.keeper.spring.boot;
1818

1919
import lombok.Data;
20+
import org.springframework.beans.factory.DisposableBean;
2021
import org.springframework.boot.context.properties.ConfigurationProperties;
2122

2223
import java.util.List;
@@ -29,8 +30,11 @@
2930
*/
3031
@Data
3132
@ConfigurationProperties(prefix = "redis-keeper.redisson")
32-
public class RedissonDatasourceProperties {
33-
33+
public class RedissonDatasourceProperties implements DisposableBean {
34+
/**
35+
* The Lazy refresh.
36+
*/
37+
private boolean lazyRefresh = true;
3438
/**
3539
* The Datasource.
3640
*/
@@ -41,4 +45,14 @@ public class RedissonDatasourceProperties {
4145
*/
4246
private Map<String, List<RedissonKeeperProperties>> datasources;
4347

48+
/**
49+
* Destroy.
50+
*/
51+
@Override
52+
public void destroy() {
53+
lazyRefresh = true;
54+
datasource = null;
55+
datasources = null;
56+
}
57+
4458
}

redis-keeper-support/src/main/java/org/codeba/redis/keeper/support/CacheDatasource.java

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,24 @@
1717
package org.codeba.redis.keeper.support;
1818

1919
import org.codeba.redis.keeper.core.CacheDatasourceStatus;
20+
import org.codeba.redis.keeper.core.CacheTemplate;
2021
import org.codeba.redis.keeper.core.Provider;
2122

2223
import java.util.ArrayList;
2324
import java.util.Arrays;
2425
import java.util.Collections;
2526
import java.util.HashMap;
27+
import java.util.HashSet;
28+
import java.util.Iterator;
2629
import java.util.List;
2730
import java.util.Map;
31+
import java.util.Set;
32+
import java.util.concurrent.CompletableFuture;
33+
import java.util.concurrent.ConcurrentHashMap;
34+
import java.util.concurrent.ExecutorService;
35+
import java.util.concurrent.LinkedBlockingQueue;
36+
import java.util.concurrent.ThreadPoolExecutor;
37+
import java.util.concurrent.TimeUnit;
2838
import java.util.function.Consumer;
2939
import java.util.stream.Collectors;
3040

@@ -36,6 +46,21 @@
3646
*/
3747
public interface CacheDatasource<T> {
3848

49+
/**
50+
* The constant TEMPLATE_CACHE_MAP.
51+
*/
52+
ConcurrentHashMap<String, Object> TEMPLATE_CACHE_MAP = new ConcurrentHashMap<>();
53+
54+
/**
55+
* The constant TEMPLATE_CONFIG_CACHE_SET.
56+
*/
57+
Set<String> TEMPLATE_CONFIG_CACHE_SET = Collections.synchronizedSet(new HashSet<>());
58+
59+
/**
60+
* The constant THREAD_POOL_EXECUTOR.
61+
*/
62+
ExecutorService THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5, 10, 2, TimeUnit.MINUTES, new LinkedBlockingQueue<>(100));
63+
3964
/**
4065
* Instant template t.
4166
*
@@ -51,6 +76,7 @@ public interface CacheDatasource<T> {
5176
* @return the map
5277
*/
5378
default Map<String, T> initialize(Map<String, CacheKeeperConfig> datasourceMap) {
79+
TEMPLATE_CONFIG_CACHE_SET.clear();
5480
final Map<String, T> map = new HashMap<>();
5581

5682
if (null == datasourceMap || datasourceMap.isEmpty()) {
@@ -66,7 +92,13 @@ default Map<String, T> initialize(Map<String, CacheKeeperConfig> datasourceMap)
6692
}).accept(config);
6793

6894
// cacheTemplate Instantiation
69-
final T template = instantTemplate(config);
95+
final String configString = config.toString();
96+
TEMPLATE_CONFIG_CACHE_SET.add(configString);
97+
T template = getTemplate(configString);
98+
if (null == template) {
99+
template = instantTemplate(config);
100+
TEMPLATE_CACHE_MAP.put(configString, template);
101+
}
70102

71103
// <name, template>
72104
map.put(key.trim(), template);
@@ -86,6 +118,7 @@ default Map<String, T> initialize(Map<String, CacheKeeperConfig> datasourceMap)
86118
* @return the map
87119
*/
88120
default Map<String, List<T>> initializeMulti(Map<String, List<CacheKeeperConfig>> datasourceMap) {
121+
TEMPLATE_CONFIG_CACHE_SET.clear();
89122
final Map<String, List<T>> map = new HashMap<>();
90123

91124
if (null == datasourceMap || datasourceMap.isEmpty()) {
@@ -103,7 +136,13 @@ default Map<String, List<T>> initializeMulti(Map<String, List<CacheKeeperConfig>
103136
}).accept(config);
104137

105138
// cacheTemplate Instantiation
106-
final T template = instantTemplate(config);
139+
final String configString = config.toString();
140+
TEMPLATE_CONFIG_CACHE_SET.add(configString);
141+
T template = getTemplate(configString);
142+
if (null == template) {
143+
template = instantTemplate(config);
144+
TEMPLATE_CACHE_MAP.put(configString, template);
145+
}
107146

108147
// <name-datasourceStatus, List<template>>
109148
final CacheDatasourceStatus datasourceStatus = checkDatasourceStatus(config.getStatus());
@@ -151,4 +190,37 @@ default CacheDatasourceStatus checkDatasourceStatus(String name) {
151190

152191
}
153192

193+
/**
194+
* Gets template.
195+
*
196+
* @param config the config
197+
* @return the template
198+
*/
199+
default T getTemplate(String config) {
200+
final Object object = TEMPLATE_CACHE_MAP.get(config);
201+
return null == object ? null : (T) object;
202+
}
203+
204+
/**
205+
* Clean.
206+
*/
207+
default void clean() {
208+
for (Iterator<Map.Entry<String, Object>> it = TEMPLATE_CACHE_MAP.entrySet().iterator(); it.hasNext(); ) {
209+
final Map.Entry<String, Object> entry = it.next();
210+
final String key = entry.getKey();
211+
if (!TEMPLATE_CONFIG_CACHE_SET.contains(key)) {
212+
final Object value = entry.getValue();
213+
if (null != value) {
214+
// clean async
215+
CompletableFuture.runAsync(() -> {
216+
CacheTemplate template = (CacheTemplate) value;
217+
template.destroy();
218+
template = null;
219+
}, THREAD_POOL_EXECUTOR);
220+
}
221+
it.remove();
222+
}
223+
}
224+
}
225+
154226
}

redis-keeper-support/src/main/java/org/codeba/redis/keeper/support/CacheKeeperConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.codeba.redis.keeper.core.CacheDatasourceStatus;
2020
import org.redisson.config.Config;
2121

22+
import java.io.IOException;
23+
2224
/**
2325
* The type Cache keeper config.
2426
*
@@ -79,6 +81,17 @@ public CacheKeeperConfig(String status, Config config, boolean invokeParamsPrint
7981
this.config = config;
8082
}
8183

84+
@Override
85+
public String toString() {
86+
final String configYAML;
87+
try {
88+
configYAML = config.toYAML();
89+
} catch (IOException e) {
90+
throw new RuntimeException(e);
91+
}
92+
return Utils.getMD5(status + invokeParamsPrint + configYAML);
93+
}
94+
8295
/**
8396
* Gets status.
8497
*

0 commit comments

Comments
 (0)