springboot 整合redis实现分布式锁和缓存

1. 依赖

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.28.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

2. Redisson配置

2.1 基本配置

@Slf4j
@Configuration
public class RedissonConfig {

    /**
     * redisson协议前缀
     */
    private static final String SCHEMA_PREFIX = "redis://";

    /**
     * 锁超时时间
     */
    private long lockWatchTimeOut = 30000;

    @Bean
    public RedissonClient redissonClient(RedisProperties redisProperties) {
        Config config = new Config();
        RedisProperties.Sentinel sentinel = redisProperties.getSentinel();
        RedisProperties.Cluster redisPropertiesCluster = redisProperties.getCluster();
        if (redisPropertiesCluster != null) {
            //集群redis
            ClusterServersConfig clusterServersConfig = config.useClusterServers();
            for (String cluster : redisPropertiesCluster.getNodes()) {
                clusterServersConfig.addNodeAddress(SCHEMA_PREFIX + cluster);
            }
            if (StringUtils.hasText(redisProperties.getPassword())) {
                clusterServersConfig.setPassword(redisProperties.getPassword());
            }
            // clusterServersConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
            clusterServersConfig.setPingConnectionInterval(30000);
        } else if (StringUtils.hasText(redisProperties.getHost())) {
            //单点redis
            SingleServerConfig singleServerConfig = config.useSingleServer().
                    setAddress(SCHEMA_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort());
            if (StringUtils.hasText(redisProperties.getPassword())) {
                singleServerConfig.setPassword(redisProperties.getPassword());
            }
            // singleServerConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
            singleServerConfig.setPingConnectionInterval(30000);
            singleServerConfig.setDatabase(redisProperties.getDatabase());
        } else if (sentinel != null) {
            //哨兵模式
            SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
            sentinelServersConfig.setMasterName(sentinel.getMaster());
            for (String node : sentinel.getNodes()) {
                sentinelServersConfig.addSentinelAddress(SCHEMA_PREFIX + node);
            }
            if (StringUtils.hasText(redisProperties.getPassword())) {
                sentinelServersConfig.setPassword(redisProperties.getPassword());
            }
            // sentinelServersConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
            sentinelServersConfig.setPingConnectionInterval(30000);
            sentinelServersConfig.setDatabase(redisProperties.getDatabase());
        }
        config.setLockWatchdogTimeout(lockWatchTimeOut);
        return Redisson.create(config);
    }
}

2.2 使用

    @Autowired
    private RedissonClient redissonClient;
    
    // 获取锁
    RLock rlock = redissonClient.getLock(this.getClass().toString());
    boolean lock = rlock.tryLock();
    // 释放锁
    lock.unlock();

3. Redis配置

3.1 基本配置

@Slf4j
@Configuration
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    @Primary
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        RedisSerializerUtil.setRedisSerializer(redisTemplate);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisService redisService(RedisTemplate<String, Object> redisTemplate) {
        return new RedisService(redisTemplate);
    }
}

3.2 可选数据库配置

有时候我们使用的缓存不一定在同一个数据库,所以有了这个配置

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisHelper {
    private final RedisProperties redisProperties;

    public RedisHelper(RedisProperties redisProperties) {
        this.redisProperties = redisProperties;
    }

    ConcurrentHashMap<Integer, RedisTemplate<String, Object>> templateMap = new ConcurrentHashMap<>();

    public RedisService database(Integer database) {
        if (database < 0 || database > 15) throw new CacheException("redis数据库配置错误!");
        RedisTemplate<String, Object> redisTemplate;
        if (templateMap.containsKey(database)) {
            redisTemplate = templateMap.get(database);
        } else {
            redisTemplate = RedisTemplateFactory.createRedisTemplate(redisProperties.getHost(), redisProperties.getPort(), redisProperties.getPassword(), database);
            RedisSerializerUtil.setRedisSerializer(redisTemplate);
            templateMap.put(database, redisTemplate);
        }
        return new RedisService(redisTemplate);
    }
}
public class RedisTemplateFactory {

    public static RedisTemplate<String, Object> createRedisTemplate(String host, Integer port, String password, Integer database) {
        return createRedisTemplate(host, port, password, database, 8, 8, -1L, 0);
    }

    public static RedisTemplate<String, Object> createRedisTemplate(String host, Integer port, String password, Integer database, Integer maxActive, Integer maxIdle, Long maxWait, Integer minIdle) {
        GenericObjectPoolConfig poolConfig = localPoolConfig(maxActive, maxIdle, maxWait, minIdle);
        RedisStandaloneConfiguration redisConfig = localRedisConfig(host, port, password, database);
        LettuceConnectionFactory connectionFactory = localLettuceConnectionFactory(redisConfig, poolConfig);
        return localRedisTemplate(connectionFactory);
    }

    /**
     * 连接池属性
     *
     * @param maxActive
     * @param maxIdle
     * @param maxWait
     * @param minIdle
     * @return
     */
    public static GenericObjectPoolConfig localPoolConfig(Integer maxActive, Integer maxIdle, Long maxWait, Integer minIdle) {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxTotal(maxActive);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setMaxWaitMillis(maxWait);
        return config;
    }

    /**
     * Redis配置
     *
     * @param host
     * @param port
     * @param password
     * @param database
     * @return
     */
    public static RedisStandaloneConfiguration localRedisConfig(String host, Integer port, String password, Integer database) {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPassword(RedisPassword.of(password));
        config.setPort(port);
        config.setDatabase(database);
        return config;
    }

    /**
     * 根据Lettuce连接工厂创建RedisTemplate
     *
     * @param localLettuceConnectionFactory
     * @return
     */
    public static RedisTemplate<String, Object> localRedisTemplate(LettuceConnectionFactory localLettuceConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(localLettuceConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 根据 连接池配置、Redis 配置创建连接工厂
     *
     * @param localRedisConfig
     * @param localPoolConfig
     * @return
     */
    public static LettuceConnectionFactory localLettuceConnectionFactory(RedisStandaloneConfiguration localRedisConfig, GenericObjectPoolConfig localPoolConfig) {
        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100)).poolConfig(localPoolConfig).build();
        LettuceConnectionFactory factory = new LettuceConnectionFactory(localRedisConfig, clientConfig);
        factory.afterPropertiesSet();
        return factory;
    }
}

3.3 注入RedisService

public class RedisService {
    public final RedisTemplate redisTemplate;

    public RedisService(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }


    /**
     * 查找key
     * redisTemplate的opsForHash,opsForSet,opsForZSet 可以 分别对应 sscan、hscan、zscan
     *
     * @param matchKey
     * @return
     */
    public Set<String> scan(String matchKey) {
        return (HashSet<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            HashSet<String> hashSet = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(matchKey).build());
            while (cursor.hasNext()) {
                byte[] next = cursor.next();
                String s = new String(next);
                hashSet.add(s);
            }
            return hashSet;
        });
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public long getExpire(final String key) {
        return redisTemplate.getExpire(key);
    }

    // 普通对象

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false 失败
     */
    public boolean setNx(String key, Object value) {
        return redisTemplate.opsForValue().setIfPresent(key, value);
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean setNx(String key, Object value, long time) {
        return redisTemplate.opsForValue().setIfPresent(key, value, time, TimeUnit.SECONDS);
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decrement(String key, long delta) {
        return redisTemplate.opsForValue().decrement(key, -delta);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void setObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     * @param time  时间
     */
    public <T> void setObject(final String key, final T value, final long time) {
        redisTemplate.opsForValue().set(key, value);
        if (time > 0) {
            expire(key, time);
        }
    }


    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean delObject(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public boolean delObject(final Collection collection) {
        return redisTemplate.delete(collection) > 0;
    }


    // list类型

    /**
     * 缓存List数据
     *
     * @param key  缓存的键值
     * @param list 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setList(final String key, final List<T> list) {
        Long count = redisTemplate.opsForList().rightPushAll(key, list);
        return count == null ? 0 : count;
    }

    /**
     * 缓存List数据
     *
     * @param key  缓存的键值
     * @param list 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setList(final String key, final List<T> list, final long time) {
        Long count = redisTemplate.opsForList().rightPushAll(key, list);
        if (time > 0) {
            expire(key, time);
        }
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 获得缓存的list对象
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1 代表所有值
     * @return
     */
    public List<Object> getList(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return
     */
    public long getListSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public <T> T getIndexList(String key, long index) {
        return (T) redisTemplate.opsForList().index(key, index);
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public <T> void updateIndexList(String key, long index, T value) {
        redisTemplate.opsForList().set(key, index, value);
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public <T> long delList(String key, long count, T value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }

    // set类型

    /**
     * 缓存Set
     *
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setSetValue(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        for (T t : dataSet) {
            setOperation.add(t);
        }
        return setOperation;
    }

    /**
     * 缓存Set
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long setSetAndTime(String key, long time, Object... values) {
        long count = redisTemplate.opsForSet().add(key, values);
        if (time > 0) {
            expire(key, time);
        }
        return count;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getSetValue(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean hasSetKeyValue(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return
     */
    public long getSetSize(String key) {
        return redisTemplate.opsForSet().size(key);

    }

    /**
     * 删除set的值
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long delSetValues(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    // zSet 有序集合

    /**
     * 添加有序集合
     *
     * @param key
     * @param score
     * @param val
     * @param time
     */
    public void setZSetValue(String key, Object val, double score, long time) {
        redisTemplate.opsForZSet().add(key, val, score);
        this.expire(key, time);
    }

    /**
     * 取出所有有序集合,进行降序排序
     *
     * @param key
     * @return
     */
    public Set<Object> getZSetValue(String key) {
        return redisTemplate.opsForZSet().reverseRange(key, 0, -1);
    }

    /**
     * 取出部分有序集合,进行降序排序
     *
     * @param key
     * @return
     */
    public Set<Object> getZSetValue(String key, Integer start, Integer end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 获取zSet总条数
     *
     * @param key
     */
    public long getZSetSize(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 增量,若key不存在则新建并添加数据,否则累加
     *
     * @param key
     * @param val
     * @param score
     * @param time
     */
    public void incrementZSetScore(String key, Object val, double score, long time) {
        redisTemplate.opsForZSet().incrementScore(key, val, score);
        this.expire(key, time);
    }


    // map类型

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setHashMap(final String key, final Map<String, T> dataMap) {
        redisTemplate.opsForHash().putAll(key, dataMap);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setHashMap(final String key, final Map<String, T> dataMap, final long time) {
        redisTemplate.opsForHash().putAll(key, dataMap);
        if (time > 0) {
            expire(key, time);
        }
    }

    /**
     * 往Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value 值
     */
    public <T> void setHashValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value 值
     * @param time  时间 秒
     */
    public <T> void setHashValue(final String key, final String hKey, final T value, final long time) {
        redisTemplate.opsForHash().put(key, hKey, value);
        if (time > 0) {
            expire(key, time);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getHashMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getHashValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiHashValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 删除Hash中的某条数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    public boolean delHashValue(final String key, final String hKey) {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }

    /**
     * 删除Hash中的某条数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    public boolean delHashValues(final String key, final Object... hKey) {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  redis键
     * @param hKey hash键
     * @return true 存在 false不存在
     */
    public boolean hasHashKey(String key, String hKey) {
        return redisTemplate.opsForHash().hasKey(key, hKey);
    }

    /**
     * hash递增
     *
     * @param key   键
     * @param hKey  项
     * @param value 要增加的值(大于0)
     * @return
     */
    public double incrementHash(String key, String hKey, double value) {
        return redisTemplate.opsForHash().increment(key, hKey, value);
    }
}

3.4 使用

    // 直接使用
    private RedisService redisService;

    // 切换数据库
    @Autowired
    private RedisHelper redisHelper;

    Boolean boolean = redisHelper.database(1).hasKey("keyName");

3.5 yaml

# spring配置
spring:
  redis:
    host: localhost
    port: 6379
    timeout: 180000      # 连接超时时间(毫秒)
    database: 0         # Redis默认情况下有16个分片,这里配置具体使用的分片
    lettuce:
      pool:
        max-active: 100   # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-wait: -1    # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-idle: 8     # 连接池中的最大空闲连接 默认 8
        min-idle: 0     # 连接池中的最小空闲连接 默认 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hlsd#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值