Skip to content

Commit 2876667

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 3d3ad89 commit 2876667

File tree

12 files changed

+452
-83
lines changed

12 files changed

+452
-83
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
@@ -3655,4 +3655,9 @@ public interface CacheTemplate {
36553655
*/
36563656
CompletableFuture<Boolean> tryAcquireAsync(String key, long permits);
36573657

3658+
/**
3659+
* Destroy.
3660+
*/
3661+
void destroy();
3662+
36583663
}

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

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
11
/*
22
* Copyright (c) 2024-2025, redis-keeper ([email protected])
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616

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 {
@@ -39,12 +42,38 @@ public class TestController {
3942
* Test refresh.
4043
*/
4144
@RequestMapping("/refresh")
42-
public void testRefresh() {
43-
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds1");
44-
templateOptional.ifPresent(cacheTemplate -> {
45-
cacheTemplate.set("foo", "bar");
46-
cacheTemplate.del("foo");
45+
public boolean testRefresh() {
46+
final long start = System.currentTimeMillis();
47+
48+
final CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {
49+
for (int i = 0; i < 100000; i++) {
50+
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds4");
51+
templateOptional.ifPresent(cacheTemplate -> {
52+
cacheTemplate.incr("testRefresh");
53+
});
54+
}
55+
});
56+
final CompletableFuture<Void> f2 = CompletableFuture.runAsync(() -> {
57+
for (int i = 0; i < 100000; i++) {
58+
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds4");
59+
templateOptional.ifPresent(cacheTemplate -> {
60+
cacheTemplate.incr("testRefresh");
61+
});
62+
}
63+
});
64+
final CompletableFuture<Void> f3 = CompletableFuture.runAsync(() -> {
65+
for (int i = 0; i < 100000; i++) {
66+
final Optional<CacheTemplate> templateOptional = provider.getTemplate("ds4");
67+
templateOptional.ifPresent(cacheTemplate -> {
68+
cacheTemplate.incr("testRefresh");
69+
});
70+
}
4771
});
72+
73+
CompletableFuture.allOf(f1, f2, f3).join();
74+
log.info("cost time: {}", (System.currentTimeMillis() - start));
75+
76+
return true;
4877
}
4978

5079

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
/*
22
* Copyright (c) 2024-2025, redis-keeper ([email protected])
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616

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,10 +30,33 @@
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, when a configuration change occurs, the bean initialized immediately or when the bean is first invoked
37+
* <p>
38+
* true: the bean initialized when the bean is first invoked
39+
* false: the bean initialized immediately
40+
*/
41+
private boolean lazyRefresh = true;
42+
/**
43+
* The Datasource. One-to-one redis data source
44+
*/
3445
private Map<String, RedisKeeperProperties> datasource;
3546

47+
/**
48+
* The Datasources. One-to-many redis data sources
49+
*/
3650
private Map<String, List<RedisKeeperProperties>> datasources;
3751

52+
/**
53+
* Destroy.
54+
*/
55+
@Override
56+
public void destroy() {
57+
lazyRefresh = true;
58+
datasource = null;
59+
datasources = null;
60+
}
61+
3862
}

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

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
/*
22
* Copyright (c) 2024-2025, redis-keeper ([email protected])
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616

1717
package org.codeba.redis.keeper.spring.boot;
@@ -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,28 +60,69 @@
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

74+
/**
75+
* The Cache datasource.
76+
*/
6077
@Autowired(required = false)
6178
private CacheDatasource<T> cacheDatasource;
6279

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+
6392
/**
6493
* Cache template provider cache template provider.
6594
*
66-
* @param redisProperties the redis properties
67-
* @param redissonProperties the redisson properties
6895
* @return the cache template provider
6996
* @throws IOException the io exception
7097
*/
71-
@Bean
98+
@Bean(name = RedisKeeperAutoConfiguration.PROVIDER_BEAN_NAME)
7299
@RefreshScope
73-
public CacheTemplateProvider<T> cacheTemplateProvider(RedisDatasourceProperties redisProperties, RedissonDatasourceProperties redissonProperties) throws IOException {
100+
public CacheTemplateProvider<T> cacheTemplateProvider() throws IOException {
74101
final Map<String, T> loadMap = load(redisProperties, redissonProperties);
75-
final Map<String, List<T>> loadsMap = loads(redisProperties, redissonProperties);
76-
77-
return new CacheTemplateProvider<>(loadMap, loadsMap);
102+
final Map<String, List<T>> loadListMap = loads(redisProperties, redissonProperties);
103+
// clean
104+
cacheDatasource.clean();
105+
return new CacheTemplateProvider<>(loadMap, loadListMap);
78106
}
79107

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

118+
/**
119+
* Load map.
120+
*
121+
* @param redisProperties the redis properties
122+
* @param redissonProperties the redisson properties
123+
* @return the map
124+
* @throws IOException the io exception
125+
*/
81126
private Map<String, T> load(RedisDatasourceProperties redisProperties, RedissonDatasourceProperties redissonProperties) throws IOException {
82127
if (null == cacheDatasource) {
83128
cacheDatasource = (CacheDatasource<T>) new DefaultCacheDatasource();
@@ -116,6 +161,13 @@ private Map<String, T> load(RedisDatasourceProperties redisProperties, RedissonD
116161
return cacheDatasource.initialize(map);
117162
}
118163

164+
/**
165+
* Loads map.
166+
*
167+
* @param redisProperties the redis properties
168+
* @param redissonProperties the redisson properties
169+
* @return the map
170+
*/
119171
private Map<String, List<T>> loads(RedisDatasourceProperties redisProperties, RedissonDatasourceProperties redissonProperties) {
120172
if (null == cacheDatasource) {
121173
cacheDatasource = (CacheDatasource<T>) new DefaultCacheDatasource();
@@ -175,6 +227,13 @@ private Map<String, List<T>> loads(RedisDatasourceProperties redisProperties, Re
175227
return cacheDatasource.initializeMulti(map);
176228
}
177229

230+
/**
231+
* Config config.
232+
*
233+
* @param redissonKeeperProperties the redisson keeper properties
234+
* @return the config
235+
* @throws IOException the io exception
236+
*/
178237
private Config config(RedissonKeeperProperties redissonKeeperProperties) throws IOException {
179238
Config result = null;
180239
final String config = redissonKeeperProperties.getConfig();
@@ -187,6 +246,13 @@ private Config config(RedissonKeeperProperties redissonKeeperProperties) throws
187246
return result;
188247
}
189248

249+
/**
250+
* Config config.
251+
*
252+
* @param redisProperties the redis properties
253+
* @return the config
254+
* @throws IOException the io exception
255+
*/
190256
private Config config(RedisProperties redisProperties) throws IOException {
191257
Config config;
192258
Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
/*
22
* Copyright (c) 2024-2025, redis-keeper ([email protected])
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616

1717
package org.codeba.redis.keeper.spring.boot;
@@ -30,6 +30,21 @@
3030
@Data
3131
@EqualsAndHashCode(callSuper = true)
3232
public class RedisKeeperProperties extends RedisProperties {
33+
/**
34+
* The Status is the identity of the data source, you can specify the identity through the cacheProvider class to get the corresponding data source
35+
* <p>
36+
* Value range: RO, WO, RW, SKIP
37+
* RO: Read-only cache datasource status
38+
* WO: Write-only cache datasource status.
39+
* RW: Read-write cache datasource status.
40+
* SKIP: Skip cache datasource status.
41+
*/
3342
private String status = CacheDatasourceStatus.RW.name();
43+
/**
44+
* The Invoke params print, means whether to print an entry log when executing the methods of the cacheTemplate class.
45+
* <p>
46+
* true : print entry log
47+
* false: not print entry log
48+
*/
3449
private boolean invokeParamsPrint;
3550
}

0 commit comments

Comments
 (0)