redis分布式锁实现的两种方式 lua脚本与redisson,解决自定义超时后锁自动续期失效问题

        分布式锁可以解决日常分布式系统多不同进程访问同一个共享资源导致资源失去原子性的问题,保证这个共享的资源在同一时间只能被一个线程操作。因为它是一个跨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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值