背景
Lettuce连接池
再common包中,增加redis的配置类,并通过META-INF.spring注入到spring容器中
原来代码
/*
* Copyright (c) 2020 jm4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.demo.common.core.config.redis;
import com.demo.common.core.constant.CacheConstants;
import com.demo.common.core.constant.CommonConstants;
import lombok.Data;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
/**
* @author jm-wss
* @date 2019/2/1 Redis 配置类
*/
@Data
@EnableCaching
@AutoConfiguration
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisTemplateConfiguration {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private Integer port;
@Value("${spring.redis.database}")
private Integer database;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.lettuce.pool.max-active:-1}")
private Integer maxActive;
@Value("${spring.redis.lettuce.pool.max-idle:16}")
private Integer maxIdle;
@Value("${spring.redis.lettuce.pool.min-idle:0}")
private Integer minIdle;
@Value("${spring.redis.lettuce.pool.max-wait:5000}")
private Long maxWait;
@Value("${spring.redis.lettuce.pool.shutdown-timeout:600}")
private Integer shutdownTimeout;
//秒
@Value("${spring.redis.password.lettuce.pool.timeout:600}")
private Integer timeout;
@Primary
@Bean("lettuceConnectionFactory")
public LettuceConnectionFactory lettuceConnectionFactory(@Qualifier("redisSentinelConfiguration") RedisStandaloneConfiguration redisSentinelConfiguration
, @Qualifier("lettuceClientConfiguration") LettuceClientConfiguration lettuceClientConfiguration) {
return new LettuceConnectionFactory(redisSentinelConfiguration,lettuceClientConfiguration);
}
@Primary
@Bean("genericObjectPoolConfig")
public GenericObjectPoolConfig genericObjectPoolConfig() {
GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxTotal(maxActive);
poolConfig.setMaxWaitMillis(maxWait);
// 关键配置:确保连接池可以快速关闭
poolConfig.setBlockWhenExhausted(false); // 当连接池耗尽时不阻塞
poolConfig.setTestOnBorrow(true); // 借出连接时测试有效性
poolConfig.setTestOnReturn(true); // 归还连接时测试有效性
poolConfig.setTestWhileIdle(true); // 空闲时测试连接
poolConfig.setTimeBetweenEvictionRunsMillis(30000); // 30秒检查一次
poolConfig.setMinEvictableIdleTimeMillis(20000); // 空闲60秒可回收
return poolConfig;
}
@Primary
@Bean("lettuceClientConfiguration")
public LettuceClientConfiguration lettuceClientConfiguration(@Qualifier("genericObjectPoolConfig") GenericObjectPoolConfig genericObjectPoolConfig) {
return LettucePoolingClientConfiguration.builder()
.poolConfig(genericObjectPoolConfig)
.commandTimeout(Duration.ofSeconds(timeout))
.shutdownTimeout(Duration.ofSeconds(shutdownTimeout))
// 关键配置:设置优雅关闭超时
.shutdownQuietPeriod(Duration.ofSeconds(2))
.shutdownTimeout(Duration.ofSeconds(5))
.build();
}
@Primary
@Bean("redisSentinelConfiguration")
public RedisStandaloneConfiguration redisSentinelConfiguration() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(database);
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setPort(port);
return redisStandaloneConfiguration;
}
/**
* LettuceConnectionFactory 默认使用
* @param factory
* @return
*/
// @Primary
@Bean(name = CommonConstants.DEFAULT_REDIS)
public RedisTemplate<String, Object> redisTemplate(@Qualifier(value = "lettuceConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.java());
redisTemplate.setHashValueSerializer(RedisSerializer.java());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Bean(name = CacheConstants.redisTemplateJson)
public RedisTemplate<Object, Object> redisTemplateJson(@Qualifier(value = "lettuceConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Primary
@Bean(name = "stringRedisTemplate")
public StringRedisTemplate stringRedisTemplate(@Qualifier(value = "lettuceConnectionFactory") RedisConnectionFactory factory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setKeySerializer(RedisSerializer.string());
stringRedisTemplate.setValueSerializer(stringSerializer());
stringRedisTemplate.setConnectionFactory(factory);
return stringRedisTemplate;
}
//自定义json序列化
public static RedisSerializer stringSerializer() {
return new RedisSerializer<Object>() {
@Override
public byte[] serialize(Object o) throws SerializationException {
if(null == o){
return new byte[0];
}
return o.toString().getBytes(StandardCharsets.UTF_8);
}
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
return new String(bytes, StandardCharsets.UTF_8);
}
};
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
@Bean
public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
return redisTemplate.opsForValue();
}
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
@Bean
public CacheManager oneDayCacheManager(@Qualifier(value = "lettuceConnectionFactory") RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.java()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
@Bean(name = "flushDBScript")
public DefaultRedisScript<Long> flushDBScript() {
String script = "return redis.call('flushdb')";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
return redisScript;
}
@Bean(name = "checkSetHasScript")
public DefaultRedisScript<List> checkSetHasScript() {
String script = "local key = KEYS[1]\n" +
"local members = ARGV\n" +
"local exists = {}\n" +
" \n" +
"for i, member in ipairs(members) do\n" +
" exists[i] = redis.call('SISMEMBER', key, member)\n" +
"end\n" +
"return exists";
DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(script, List.class);
return redisScript;
}
@Bean(name = "matchDelScript")
public DefaultRedisScript<Long> matchDelScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(matchDelScriptText());
redisScript.setResultType(Long.class);
return redisScript;
}
private String matchDelScriptText(){
return "local key=KEYS[1]\n" +
"local result = 0;\n" +
"local list=redis.call(\"keys\", key);\n" +
"for i,v in ipairs(list) do\n" +
" local current =redis.call(\"del\", v);\n" +
" result = result + current;\n" +
"end\n" +
"return result;";
}
@Bean
public DefaultRedisScript<Long> limitScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(limitScriptText());
redisScript.setResultType(Long.class);
return redisScript;
}
/**
* 限流脚本
*/
private String limitScriptText() {
return "local key = KEYS[1]\n" +
"local count = tonumber(ARGV[1])\n" +
"local time = tonumber(ARGV[2])\n" +
"local current = redis.call('get', key);\n" +
"if current and tonumber(current) > count then\n" +
" return tonumber(current);\n" +
"end\n" +
"current = redis.call('incr', key)\n" +
"if tonumber(current) == 1 then\n" +
" redis.call('expire', key, time)\n" +
"end\n" +
"return tonumber(current);";
}
@Bean(name = "apiLimitRedisScript")
public DefaultRedisScript<Long> apiLimitRedisScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(apiLimitScriptStr());
redisScript.setResultType(Long.class);
return redisScript;
}
private static String apiLimitScriptStr() {
return "local key = KEYS[1]\n" +
"local a1 = ARGV[1]\n" +
"local a2 = ARGV[2]\n" +
"local count = tonumber(a1)\n" +
"local time = tonumber(a2)\n" +
"\n" +
"if key == nil or count == nil or time == nil then\n" +
" error(\"param is null\")\n" +
"end\n" +
"\n" +
"local current = redis.call('get', key);\n" +
"if current and current ~= nil and tonumber(current) > count then\n" +
" return tonumber(current)\n" +
"end\n" +
"current = redis.call('incr', key);\n" +
"if tonumber(current) == 1 then \n" +
" redis.call('EXPIRE',key,time)\n" +
"end\n" +
"return tonumber(current)";
}
@Bean(name = "bloomAddDataLimitScript")
public DefaultRedisScript<Object> bloomAddDataLimitScript() {
DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(bloomAddData());
redisScript.setResultType(Object.class);
return redisScript;
}
private static String bloomAddData() {
return "local key = KEYS[1]\n" +
"\n" +
"if key == nil then\n" +
" error(\"param is null\")\n" +
"end\n" +
"local result_1 = 0\n" +
"for k,v in ipairs(ARGV) do\n" +
" local result_2 = redis.call('SETBIT',key,v,1)\n" +
" result_1 = result_1 + result_2\n" +
"end\n" +
"return tostring(result_1)\n"
;
}
@Bean(name = "tokenBucketLimitScript")
public DefaultRedisScript<Long> tokenBucketLimitScript() {
return new DefaultRedisScript<>(TOKEN_BUCKET_LIMIT_SCRIPT,Long.class);
}
private static final String TOKEN_BUCKET_LIMIT_SCRIPT = "local tokens_key = KEYS[1]\n" +
"local timestamp_key = KEYS[2]\n" +
"local rate = tonumber(ARGV[1])\n" +
"local capacity = tonumber(ARGV[2])\n" +
"local now = tonumber(ARGV[3])\n" +
"local requested = tonumber(ARGV[4])\n" +
"local fill_time = capacity/rate\n" +
"local ttl = math.floor(fill_time*2)\n" +
"local last_tokens = tonumber(redis.call('get', tokens_key))\n" +
"if last_tokens == nil then\n" +
" last_tokens = capacity\n" +
"end\n" +
"local last_refreshed = tonumber(redis.call('get', timestamp_key))\n" +
"if last_refreshed == nil then\n" +
" last_refreshed = 0\n" +
"end\n" +
"local diff_time = math.max(0, now-last_refreshed)\n" +
"local filled_tokens = math.min(capacity, last_tokens+(diff_time*rate))\n" +
"local allowed = filled_tokens >= requested\n" +
"local new_tokens = filled_tokens\n" +
"local allowed_num = 0\n" +
"if allowed then\n" +
" new_tokens = filled_tokens - requested\n" +
" allowed_num = 1\n" +
"end\n" +
"if ttl > 0 then\n" +
" redis.call('setex', tokens_key, ttl, new_tokens)\n" +
" redis.call('setex', timestamp_key, ttl, now)\n" +
"end\n" +
"return allowed_num\n";
}

kill PID 关闭springboot项目的时候,不使用-9,LettuceConnectionFactory调用.destroy();方法,进入堵塞,没办法关闭
原因:
RedisConfigurationV2是通过自己注入到spring容器中的,spring没有正常托管该类的生命周期状态,需要自己释放对象
package com.demo.common.core.config.redis;
import com.google.common.base.Charsets;
import com.google.common.hash.Funnel;
import com.demo.common.core.config.bloom.BloomFilterHelper;
import com.demo.common.core.config.bloom.RedisBloomFilter;
import com.demo.common.core.config.bloom.RedisBloomUtils;
import com.demo.common.core.constant.CacheConstants;
import io.lettuce.core.resource.ClientResources;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import javax.annotation.PreDestroy;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@Data
@Slf4j
@AutoConfiguration
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfigurationV2 implements DisposableBean {
@Value("${redis2.host}")
private String host2;
@Value("${redis2.port}")
private Integer port2;
@Value("${redis2.database}")
private Integer database2;
@Value("${redis2.password}")
private String password2;
@Value("${redis2.client.threadPoolSize:4}")
private Integer clientThreadPoolSize;
@Value("${redis2.client.cThreadPoolSize:4}")
private Integer clientCThreadPoolSize;
@Value("${redis2.lettuce.pool.max-active:-1}")
private Integer maxActive;
@Value("${redis2.lettuce.pool.max-idle:16}")
private Integer maxIdle;
@Value("${redis2.lettuce.pool.min-idle:0}")
private Integer minIdle;
@Value("${redis2.lettuce.pool.max-wait:5000}")
private Long maxWait;
//秒
@Value("${redis2.lettuce.shutdown-timeout:600}")
private Integer shutdownTimeout;
//秒
@Value("${redis2.lettuce.timeout:600}")
private Integer timeout;
private LettuceConnectionFactory lettuceConnectionFactory;
private ClientResources clientResources; // 显式管理ClientResources
private final AtomicBoolean destroyed = new AtomicBoolean(false);
@Bean("lettuceConnectionFactoryV2")
public LettuceConnectionFactory lettuceConnectionFactoryV2(
@Qualifier("redisSentinelConfigurationV2") RedisStandaloneConfiguration redisConfig,
@Qualifier("lettuceClientConfigurationV2") LettuceClientConfiguration lettuceConfig) {
if ("-1".equals(host2)) {
return null;
}
this.lettuceConnectionFactory = new LettuceConnectionFactory(redisConfig, lettuceConfig);
return this.lettuceConnectionFactory;
}
// 添加销毁方法
@PreDestroy
public void preDestroy() {
log.info("preDestroy");
shutdownRedisResources();
}
// 双保险机制:实现DisposableBean接口
@Override
public void destroy() throws Exception {
log.info("destroy");
shutdownRedisResources();
}
private static RedisConfigurationV2 instance;
public RedisConfigurationV2() {
instance = this; // 保存当前实例的引用
}
// 双保险机制:静态关闭钩子
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (instance != null) {
instance.shutdownRedisResources();
}
}));
}
/**
* 安全关闭Redis资源(带超时和强制终止)
*/
/**
* 安全关闭Redis资源(带超时和强制终止)
*/
private synchronized void shutdownRedisResources() {
if (destroyed.get()) return;
destroyed.set(true);
if (lettuceConnectionFactory == null) return;
log.info("Attempting to close Redis2 connection resources...");
// 1. 尝试关闭连接工厂
tryGracefulShutdown();
// 2. 强制关闭连接池
forceCloseConnectionPool();
// 3. 清理Lettuce资源(使用显式管理的ClientResources)
cleanupLettuceResources();
log.info("Redis2 resources released");
}
/**
* 尝试优雅关闭连接
*/
private void tryGracefulShutdown() {
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
try {
lettuceConnectionFactory.destroy();
log.info("Redis2 connections closed gracefully");
} catch (Exception e) {
log.warn("Graceful shutdown failed: {}", e.getMessage());
}
}).get(15, TimeUnit.SECONDS); // 设置15秒超时
executor.shutdownNow();
} catch (Exception e) {
log.warn("Graceful shutdown timed out: {}", e.getMessage());
}
}
/**
* 强制关闭连接池
*/
private void forceCloseConnectionPool() {
try {
// 直接关闭连接池(不依赖反射)
if (lettuceConnectionFactory != null) {
// 调用resetConnection()释放所有连接
lettuceConnectionFactory.resetConnection();
// 关闭连接池(如果可用)
// if (lettuceConnectionFactory.getPool() != null) {
// lettuceConnectionFactory.getPool().close();
// log.info("Forcibly closed connection pool");
// }
}
} catch (Exception e) {
log.warn("Force closure of connection pool failed: {}", e.getMessage());
}
}
/**
* 清理Lettuce底层资源
*/
private void cleanupLettuceResources() {
if (clientResources != null) {
try {
clientResources.shutdown().get(5, TimeUnit.SECONDS);
log.info("Lettuce client resources shutdown");
} catch (Exception e) {
log.warn("Failed to shutdown Lettuce client resources: {}", e.getMessage());
}
}
}
// 显式创建并管理ClientResources
@Bean("clientResourcesV2")
public ClientResources clientResourcesV2() {
this.clientResources = ClientResources.builder()
.ioThreadPoolSize(clientThreadPoolSize) // 根据需求调整
.computationThreadPoolSize(clientCThreadPoolSize) // 根据需求调整
.build();
return this.clientResources;
}
@Bean("genericObjectPoolConfigV2")
public GenericObjectPoolConfig genericObjectPoolConfigV2() {
GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxTotal(maxActive);
poolConfig.setMaxWaitMillis(maxWait);
// 关键配置:确保连接池可以快速关闭
poolConfig.setBlockWhenExhausted(false); // 当连接池耗尽时不阻塞
poolConfig.setTestOnBorrow(true); // 借出连接时测试有效性
poolConfig.setTestOnReturn(true); // 归还连接时测试有效性
poolConfig.setTestWhileIdle(true); // 空闲时测试连接
poolConfig.setTimeBetweenEvictionRunsMillis(30000); // 30秒检查一次
poolConfig.setMinEvictableIdleTimeMillis(20000); // 空闲60秒可回收
return poolConfig;
}
@Bean("lettuceClientConfigurationV2")
public LettuceClientConfiguration lettuceClientConfigurationV2(@Qualifier("genericObjectPoolConfigV2") GenericObjectPoolConfig genericObjectPoolConfig,
@Qualifier("clientResourcesV2") ClientResources clientResources) {
return LettucePoolingClientConfiguration.builder()
.poolConfig(genericObjectPoolConfig)
.commandTimeout(Duration.ofSeconds(timeout))
.shutdownTimeout(Duration.ofSeconds(shutdownTimeout))
// 关键配置:设置优雅关闭超时
.shutdownQuietPeriod(Duration.ofSeconds(2))
.shutdownTimeout(Duration.ofSeconds(5))
.clientResources(clientResources) // 使用显式管理的ClientResources
.build();
}
@Bean("redisSentinelConfigurationV2")
public RedisStandaloneConfiguration redisSentinelConfigurationV2() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(database2);
redisStandaloneConfiguration.setHostName(host2);
redisStandaloneConfiguration.setPassword(password2);
redisStandaloneConfiguration.setPort(port2);
return redisStandaloneConfiguration;
}
/**
* 注意Bean名称不能重复
* @return redisBean
*/
@Bean(name = CacheConstants.BLOOM_REDIS_TEMPLATE)
public RedisTemplate<String, Object> redisIotTemplate(@Qualifier("lettuceConnectionFactoryV2") LettuceConnectionFactory lettuceConnectionFactory) {
if("-1".equals(host2)){
return null;
}
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(serializer);
// redisTemplate.setValueSerializer(RedisSerializer.java());
redisTemplate.setHashValueSerializer(serializer);
// redisTemplate.setHashValueSerializer(RedisSerializer.java());
redisTemplate.setConnectionFactory(
lettuceConnectionFactory
);
log.info("多版本redis构建成功!!!");
return redisTemplate;
}
@Bean(name = CacheConstants.BLOOM_REDIS_TEMPLATE_STRING)
public StringRedisTemplate stringRedisTemplateIot(@Qualifier("lettuceConnectionFactoryV2") LettuceConnectionFactory factory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setKeySerializer(RedisSerializer.string());
stringRedisTemplate.setValueSerializer(stringSerializer());
stringRedisTemplate.setConnectionFactory(factory);
return stringRedisTemplate;
}
public static RedisSerializer stringSerializer() {
return new RedisSerializer<Object>() {
@Override
public byte[] serialize(Object o) throws SerializationException {
if(null == o){
return new byte[0];
}
return o.toString().getBytes(StandardCharsets.UTF_8);
}
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) {
return null;
}
return new String(bytes, StandardCharsets.UTF_8);
}
};
}
/**
* 布隆过滤器预计数据量:50000000
*/
private static final Integer expectedInsertions = 50000000;
/**
* 布隆过滤器精度
*/
private static final Double fpp = 0.00001;
/**
* 初始化布隆过滤器,放入到spring容器里面
*
* @return BloomFilterHelper1
*/
@Bean(value = "bloomFilterHelper")
public BloomFilterHelper<String> bloomFilterHelper() {
return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), expectedInsertions, fpp);
}
@Bean(value = "redisBloomFilter")
public RedisBloomFilter redisBloomFilter(@Qualifier(value = "bloomFilterHelper") BloomFilterHelper bloomFilterHelper,
@Qualifier(value = "bloomAddDataLimitScript") RedisScript<Object> bloomLimitScript,
@Qualifier(value = CacheConstants.BLOOM_REDIS_TEMPLATE_STRING) StringRedisTemplate stringRedisTemplateIot){
return new RedisBloomFilter(bloomFilterHelper,bloomLimitScript,stringRedisTemplateIot);
}
@Bean(value = "redisBloomUtils")
public RedisBloomUtils redisBloomUtils( @Qualifier(value = CacheConstants.BLOOM_REDIS_TEMPLATE_STRING) StringRedisTemplate stringRedisTemplateIot){
return new RedisBloomUtils(stringRedisTemplateIot);
}
}