分布式锁可以解决日常分布式系统多不同进程访问同一个共享资源导致资源失去原子性的问题,保证这个共享的资源在同一时间只能被一个线程操作。因为它是一个跨JVM的互斥机制,可以用来控制系统分布式部署情况下共享资源的访问
引入redis的maven依赖
<!-- redis引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
yml配置文件:
spring: redis: #密码 password: kevin #连接超时时长(毫秒) timeout: 30000 cluster: #集群节点以逗号分隔,或换行后 - 开头 nodes: - 127.0.0.1:6381 - 127.0.0.1:6382 - 127.0.0.1:6383 - 127.0.0.1:6384 - 127.0.0.1:6385 - 127.0.0.1:6386 # 获取失败 最大重定向次数 max-redirects: 3 #lettuce连接池信息 # 连接池最大连接数(使用负值表示没有限制) 默认为8 lettuce: pool: # 连接池最大连接数 max-active: 1000 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认为-1 max-wait: -1 # 连接池中的最大空闲连接 默认为8 max-idle: 200 # 连接池中的最小空闲连接 默认为 0 min-idle: 100
redis配置类
package com.liu.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.liu.redisexpired.RedisMessageListenerFactory;
import io.lettuce.core.TimeoutOptions;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.Arrays;
@Configuration
public class RedisConfig {
@Autowired
private Environment environment;
@Bean(value = "nodes")
@ConfigurationProperties(prefix = "spring.redis.cluster")
public RedisNodes nodes(){
return new RedisNodes();
}
/**
* 配置lettuce连接池
* @author kevin
* @return org.apache.commons.pool2.impl.GenericObjectPoolConfig
* @date 2022/5/26
*/
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.redis.cluster.lettuce.pool")
public GenericObjectPoolConfig<Object> redisPool() {
GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
String maxActive = environment.getProperty("spring.redis.lettuce.pool.max-active");
if(null != maxActive && !"".equals(maxActive)) {
poolConfig.setMaxTotal(Integer.parseInt(maxActive));
}
String maxWait = environment.getProperty("spring.redis.lettuce.pool.max-wait");
if(null != maxWait && !"".equals(maxWait)) {
poolConfig.setMaxWaitMillis(Integer.parseInt(maxWait));
}
String maxIdle = environment.getProperty("spring.redis.lettuce.pool.max-idle");
if(null != maxIdle && !"".equals(maxIdle)) {
poolConfig.setMaxIdle(Integer.parseInt(maxIdle));
}
String minIdle = environment.getProperty("spring.redis.lettuce.pool.min-idle");
if(null != minIdle && !"".equals(minIdle)) {
poolConfig.setMinIdle(Integer.parseInt(minIdle));
}
return new GenericObjectPoolConfig<>();
}
/**
* 配置数据源的
* @author kevin
* @return org.springframework.data.redis.connection.RedisClusterConfiguration
* @date 2022/5/26
*/
@Bean("redisClusterConfig")
@Primary
public RedisClusterConfiguration redisClusterConfig() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(
Arrays.asList(nodes().getNodes()));
redisClusterConfiguration.setPassword(environment.getProperty("spring.redis.password"));
String maxRedirects = environment.getProperty("spring.redis.cluster.max-redirects");
if(null != maxRedirects && !"".equals(maxRedirects)) {
redisClusterConfiguration.setMaxRedirects(Integer.parseInt(maxRedirects));
}
return redisClusterConfiguration;
}
/**
* 配置数据源的连接工厂
* 这里注意:需要添加@Primary 指定bean的名称,目的是为了创建不同名称的LettuceConnectionFactory
* @author kevin
* @param redisPool :
* @param redisClusterConfig :
* @return org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
* @date 2022/5/26
*/
@Bean("lettuceConnectionFactory")
@Primary
public LettuceConnectionFactory lettuceConnectionFactory(GenericObjectPoolConfig<Object> redisPool,
@Qualifier("redisClusterConfig") RedisClusterConfiguration redisClusterConfig) {
// LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
// .poolConfig(redisPool).build();
LettuceClientConfiguration clientConfiguration = getClientConfiguration(redisPool);
return new LettuceConnectionFactory(redisClusterConfig, clientConfiguration);
}
/**
* 配置数据源的RedisTemplate
* 注意:这里指定使用名称=factory 的 RedisConnectionFactory
* @author kevin
* @param redisConnectionFactory :
* @return org.springframework.data.redis.core.RedisTemplate
* @date 2022/5/26
*/
@Bean("redisTemplate")
@Primary
public RedisTemplate<String, Object> redisTemplate(@Qualifier("lettuceConnectionFactory")
RedisConnectionFactory redisConnectionFactory) {
return getRedisTemplate(redisConnectionFactory);
}
/**
* lettuce配置信息构建获取--redis集群高可用
* @author kevin
* @param redisPool :
* @return org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration
* @date 2022/5/27 12:00
*/
private LettuceClientConfiguration getClientConfiguration(GenericObjectPoolConfig<Object> redisPool) {
//支持自适应集群拓扑刷新和静态刷新源
ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
//.enablePeriodicRefresh(Duration.ofSeconds(10)) // 启用定期集群拓扑更新
.enableAllAdaptiveRefreshTriggers() // 自适应拓扑刷新
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(10)) // 自适应拓扑更新的超时时间
.build();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().timeoutOptions(
TimeoutOptions.enabled(Duration.ofSeconds(30))) // 超时修改为30秒
// .autoReconnect(false) //启用或禁用连接丢失时的自动重新连接--默认true
// .pingBeforeActivateConnection(Boolean.TRUE) //在激活连接标志之前设置 PING
// .cancelCommandsOnReconnectFailure(Boolean.TRUE) //允许在重新连接失败的情况下取消排队的命令。默认为 false
// .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS) //设置连接处于断开状态时的命令调用行为
.topologyRefreshOptions(clusterTopologyRefreshOptions) //设置ClusterTopologyRefreshOptions拓扑更新的详细控制
.build();
return LettucePoolingClientConfiguration.builder()
.poolConfig(redisPool) //设置GenericObjectPoolConfig驱动程序使用的连接池配置
//.readFrom(ReadFrom.NEAREST) //配置ReadFrom
.clientOptions(clusterClientOptions) //配置ClientOptions
.build();
}
/**
* redisTemplate redis操作工具获取
* @author kevin
* @param factory : redis连接工厂
* @return org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.Object>
* @date 2022/5/27 12:00
*/
private RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL) //过期方法
om.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRed