百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

SpringBoot 项目接入 Redis 集群(springboot访问redis集群)

mhr18 2024-11-01 12:21 24 浏览 0 评论

Hello 大家好,我是鸭血粉丝,Redis 想必大家一定不会陌生,平常工作中或多或少都会用到,不管是用来存储登录信息还是用来缓存热点数据,对我们来说都是很有帮助的。但是 Redis 的集群估计并不是每个人都会用到,因为很多业务场景或者系统都是一些简单的管理系统,并不会需要用到 Redis 的集群环境。

阿粉之前也是这样,项目中用的的 Redis 是个单机环境,但是最近随着终端量的上升,慢慢的发现单机已经快支撑不住的,所以思考再三决定将 Redis 的环境升级成集群。下面阿粉给大家介绍一下在升级的过程中项目中需要调整的地方,这篇文章不涉及集群的搭建和配置,感兴趣的同学自行搜索。

配置参数

因为这篇文章不介绍 Redis 集群的搭建,这里我们假设已经有了一个 Redis 的集群环境,我们项目中需要调整以下几个部分

  1. 修改配置参数,集群的节点和密码配置;
  2. 确保引入的 Jedis 版本支持设置密码,spring-data-redis 1.8 以上,SpringBoot 1.5 以上才支持设置密码;
  3. 注入 RedisTemplate
  4. 编写工具类;

修改配置参数

############### Redis 集群配置 #########################
spring.custome.redis.cluster.nodes=172.20.0.1:7001,172.20.0.2:7002,172.20.0.3:7003
spring.custome.redis.cluster.max-redirects=3
spring.custome.redis.cluster.max-active=500
spring.custome.redis.cluster.max-wait=-1
spring.custome.redis.cluster.max-idle=500
spring.custome.redis.cluster.min-idle=20
spring.custome.redis.cluster.timeout=3000
spring.custome.redis.cluster.password=redis.cluster.password

引入依赖(如果需要)

确保 SpringBoot 的版本大于 1.4.x 如果不是的话,采用如下配置,先排除 SpringBoot 中旧版本 Jedisspring-data-redis,再依赖高版本的 Jedisspring-data-redis

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <!-- 1.4 版本 SpringBoot 中 Jedis 不支持密码登录 -->
            <exclusions>
                <exclusion>
                    <groupId>redis.clients</groupId>
                    <artifactId>jedis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.data</groupId>
                    <artifactId>spring-data-redis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 手动依赖 Jedis 和 spring-data-redis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.8.0.RELEASE</version>
        </dependency>

注入 RedisTemplate

注入 RedisTemplate 我们需要三个组件,分别是JedisConnectionFactoryRedisClusterConfigurationJedisPoolConfig,下面是注入RedisTempalte 的代码。先根据配置创建 JedisConnectFactory 同时需要配置 RedisClusterConfigurationJedisPoolConfig,最后将JedisConnectionFactory 返回用于创建RedisTemplate


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

public class RedisClusterConfig {

    @Bean(name = "redisTemplate")
    @Primary
    public RedisTemplate redisClusterTemplate(@Value("${spring.custome.redis.cluster.nodes}") String host,
                                     @Value("${spring.custome.redis.cluster.password}") String password,
                                     @Value("${spring.custome.redis.cluster.timeout}") long timeout,
                                     @Value("${spring.custome.redis.cluster.max-redirects}") int maxRedirect,
                                     @Value("${spring.custome.redis.cluster.max-active}") int maxActive,
                                     @Value("${spring.custome.redis.cluster.max-wait}") int maxWait,
                                     @Value("${spring.custome.redis.cluster.max-idle}") int maxIdle,
                                     @Value("${spring.custome.redis.cluster.min-idle}") int minIdle) {

        JedisConnectionFactory connectionFactory =  jedisClusterConnectionFactory(host, password,
                timeout, maxRedirect, maxActive, maxWait, maxIdle, minIdle);
        return createRedisClusterTemplate(connectionFactory);
    }

    private JedisConnectionFactory jedisClusterConnectionFactory(String host, String password,
                                                                   long timeout, int maxRedirect, int maxActive, int maxWait, int maxIdle, int minIdle) {
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        List<RedisNode> nodeList = new ArrayList<>();
        String[] cNodes = host.split(",");
        //分割出集群节点
        for (String node : cNodes) {
            String[] hp = node.split(":");
            nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1])));
        }
        redisClusterConfiguration.setClusterNodes(nodeList);
        redisClusterConfiguration.setPassword(password);
        redisClusterConfiguration.setMaxRedirects(maxRedirect);

        // 连接池通用配置
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(maxIdle);
        genericObjectPoolConfig.setMaxTotal(maxActive);
        genericObjectPoolConfig.setMinIdle(minIdle);
        genericObjectPoolConfig.setMaxWaitMillis(maxWait);
        genericObjectPoolConfig.setTestWhileIdle(true);
        genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(300000);

        JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration
                .builder();
        builder.connectTimeout(Duration.ofSeconds(timeout));
        builder.usePooling();
        builder.poolConfig(genericObjectPoolConfig);
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(redisClusterConfiguration, builder.build());
        // 连接池初始化
        connectionFactory.afterPropertiesSet();

        return connectionFactory;
    }

    private RedisTemplate createRedisClusterTemplate(JedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

相关代码已经提交到GitHub,公众号回复【源码仓库】即可获取。这里一定要注意 Jedis 的 Spring-data-redis 的版本支持设置密码,毕竟生产环境是一定要配置密码的。

编写工具类

其实到这里基本上已经完成了,我们可以看到 SpringBoot 项目接入 Redis 集群还是比较简单的,而且如果之前单机环境就是采用RedisTemplate 的话,现在也就不需要编写工具类,之前的操作依旧有效。不过作为贴心的阿粉,我还是给大家准备了一个工具类,代码太长,我只贴部分,需要完成代码的可以到公众号回复【源码仓库】获取。

/**
     *  删除KEY
     * @param key
     * @return
     */
    public boolean delete(String key) {
        try {
            return getTemplate().delete(key);
        } catch (Exception e) {
            log.error("redis hasKey() is error");
            return false;
        }
    }

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {

        return key == null ? null : getTemplate().opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {

        try {
            getTemplate().opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            log.error("redis set() is error");
            return false;
        }

    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                getTemplate().opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error("redis set() is error");
            return false;
        }
    }

    /**
     * 计数器
     *
     * @param key 键
     * @return 值
     */
    public Long incr(String key) {

        return getTemplate().opsForValue().increment(key);
    }

    public Long incrBy(String key, long step) {

        return getTemplate().opsForValue().increment(key, step);
    }

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {

        return getTemplate().opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {

        return getTemplate().opsForHash().entries(key);
    }

    /**
     * 获取hashKey对应的批量键值
     * @param key
     * @param values
     * @return
     */
    public List<Object> hmget(String key, List<String> values) {

        return getTemplate().opsForHash().multiGet(key, values);
    }

上面随机列了几个方法,更多方案等待你的探索。

总结

今天阿粉给大家介绍了一下 SpringBoot 项目如何接入 Redis 集群,需要的朋友可以参考一下,不过阿粉还是要说一下,系统的设计不能过于冗余,如果短期内还能支撑业务的发展,那就暂时不要考虑太复杂,毕竟系统的架构是需要不断的完善的,不可能刚开始的时候就设计出一套很完善的系统框架。随着业务的不断发展,当真正发现单机Redis 已经无法满足业务需求的时候再接入也不迟!

相关推荐

MySQL数据库中,数据量越来越大,有什么具体的优化方案么?

个人的观点,这种大表的优化,不一定上来就要分库分表,因为表一旦被拆分,开发、运维的复杂度会直线上升,而大多数公司和开发人员是欠缺这种能力的。所以MySQL中几百万甚至小几千万的表,先考虑做单表的优化。...

Redis的Bitmap(位图):签到打卡、用户在线状态,用它一目了然

你是不是每天打开APP,第一时间就是去“签到打卡”?或者在社交软件里,看到你的朋友头像旁边亮着“在线”的绿灯?这些看似简单的功能背后,都隐藏着一个有趣而高效的数据结构。如果让你来设计一个签到系统:用户...

想知道有多少人看了你的文章?Redis HyperLogLog几KB就搞定!

作为一名内容创作者,你每天最期待的,除了文章阅读量蹭蹭上涨,是不是还特别想知道,到底有多少个“独立用户”阅读了你的文章?这个数字,我们通常称为“UV”(UniqueVisitors),它比总阅读量更...

Redis的“HyperLogLog”:统计网站日活用户,省内存又高效的神器

你可能从未听过这个拗口的名字——“HyperLogLog”,它听起来就像是某个高深莫测的数学公式。但请相信我,理解它的核心思想并不难,而且一旦你掌握了它,你会发现它在处理大数据统计问题时,简直就是“救...

阿里云国际站:为什么我的云服务器运行缓慢?

本文由【云老大】TG@yunlaoda360撰写一、网络性能瓶颈带宽不足现象:上传/下载速度慢,远程连接卡顿。排查:通过阿里云控制台查看网络流量峰值是否接近带宽上限34。解决:升级带宽(如从1M提...

Java 近期新闻:Jakarta EE 11和Spring AI更新、WildFly 36.0 Beta、Infinispan

作者|MichaelRedlich译者|明知山策划|丁晓昀OpenJDKJEP503(移除32位x86移植版本)已从“ProposedtoTarget”状态进入到“T...

腾讯云国际站:怎样设置自动伸缩应对流量高峰?

云计算平台服务以阿里云为例:开通服务与创建伸缩组:登录阿里云控制台,找到弹性伸缩服务并开通。创建伸缩组时,选择地域与可用区,定义伸缩组内最小/最大实例数,绑定已有VPC虚拟交换机。实例模板需...

【案例分享】如何利用京东云建设高可用业务架构

本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业...

Spring Security在前后端分离项目中的使用

1文章导读SpringSecurity是Spring家族中的一个安全管理框架,可以和SpringBoot项目很方便的集成。SpringSecurity框架的两大核心功能:认证和授权认证:...

Redis与Java集成的最佳实践

Redis与Java集成的最佳实践在当今互联网飞速发展的时代,缓存技术的重要性毋庸置疑。Redis作为一款高性能的分布式缓存数据库,与Java语言的结合更是如虎添翼。今天,我们就来聊聊Redis与Ja...

Redis在Java项目中的应用与数据持久化

Redis在Java项目中的应用与数据持久化Redis简介:为什么我们需要它?在Java项目中,Redis就像一位不知疲倦的快跑选手,总能在关键时刻挺身而出。作为一个内存数据库,它在处理高并发请求时表...

Redis 集群最大节点个数是多少?

Redis集群最大节点个数取决于Redis的哈希槽数量,因为每个节点可以负责多个哈希槽。在Redis3.0之前,Redis集群最多支持16384个哈希槽,因此最大节点数为16384个。但是在Redi...

Java开发岗面试宝典:分布式相关问答详解

今天千锋广州Java小编就给大家分享一些就业面试宝典之分布式相关问题,一起来看看吧!1.Redis和Memcache的区别?1、存储方式Memecache把数据全部存在内存之中,断电后会挂掉,数据不...

当Redis内存不足时,除了加内存,还有哪些曲线救国的办法?

作为“速度之王”的Redis,其高性能的秘密武器之一就是将数据存储在内存中。然而,内存资源是有限且昂贵的。当你的Redis实例开始告警“内存不足”,或者写入请求被阻塞时,最直接的解决方案似乎就是“加内...

商品详情页那么多信息,Redis的“哈希”如何优雅存储?

你每天网购时,无论是打开淘宝、京东还是拼多多,看到的商品详情页都琳琅满目:商品名称、价格、库存、图片、描述、评价数量、销量。这些信息加起来,多的惊人。那么问题来了:这些海量的商品信息,程序是去哪里取出...

取消回复欢迎 发表评论: