Redis——Springboot集成Redis集群

摘要:
序言springboot1.5.x中的默认Redis客户端由Jedis实现,springboot2.x中的缺省客户端由生菜实现。Lettuce与Jedis的比较Lettuce和Jedis都是连接到RedisServer的客户端。在实现方面,Jedis直接连接到redisserver。在多线程环境中,它不是线程安全的。除非使用连接池,否则将向每个redis实例添加物理连接。Lettuce是一个可扩展、线程安全、完全无阻塞的Redis客户端。多个线程可以共享一个RedisConnection。它使用NettyNIO框架来有效地管理多个连接,从而为构建非阻塞反应式应用程序提供异步和同步数据访问方法。
前言

在 springboot 1.5.x版本的默认的Redis客户端是 Jedis实现的,springboot 2.x版本中默认客户端是用 lettuce实现的。

Lettuce 与 Jedis 比较

  • Lettuce 和 Jedis 的都是连接 Redis Server的客户端。
  • Jedis 在实现上是直连 redis server,多线程环境下非线程安全,除非使用连接池,为每个 redis实例增加物理连接。
  • Lettuce 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。
使用Lettuce连接Redis集群
  • application文件
################ Redis 基础配置 ##############
# Redis数据库索引(默认为0)
spring.redis.database=0  
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379  
# Redis服务器连接密码(默认为空)
spring.redis.password=zwqh
# 链接超时时间 单位 ms(毫秒)
spring.redis.timeout=3000

################ Redis 线程池设置 ############## # 连接池最大连接数(使用负值表示没有限制) 默认 8 spring.redis.lettuce.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 spring.redis.lettuce.pool.max-wait=-1 # 连接池中的最大空闲连接 默认 8 spring.redis.lettuce.pool.max-idle=8 # 连接池中的最小空闲连接 默认 0 spring.redis.lettuce.pool.min-idle=0
  • 自定义 RedisTemplate

默认情况下的模板只能支持 RedisTemplate<String,String>,只能存入字符串,很多时候,我们需要自定义 RedisTemplate ,设置序列化器,这样我们可以很方便的操作实例对象。如下所示:

@Configuration
public class LettuceRedisConfig {

    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}
  • 序列化实体类

public class UserEntity implements Serializable {

    private static final long serialVersionUID = 5237730257103305078L;
    
    private Long id;
    private String userName;
    private String userSex;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getUserSex() {
        return userSex;
    }
    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }    
}
  • 单元测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootRedisApplicationTests {

    @Autowired
    private RedisTemplate<String, String> strRedisTemplate;
    @Autowired
    private RedisTemplate<String, Serializable> serializableRedisTemplate;
    
    @Test
    public void testString() {
        strRedisTemplate.opsForValue().set("strKey", "zwqh");
        System.out.println(strRedisTemplate.opsForValue().get("strKey"));
    }
    
    @Test
    public void testSerializable() {
        UserEntity user=new UserEntity();
        user.setId(1L);
        user.setUserName("朝雾轻寒");
        user.setUserSex("");        
        serializableRedisTemplate.opsForValue().set("user", user);        
        UserEntity user2 = (UserEntity) serializableRedisTemplate.opsForValue().get("user");
        System.out.println("user:"+user2.getId()+","+user2.getUserName()+","+user2.getUserSex());
    }

}
  • 执行结果如下:

Redis——Springboot集成Redis集群第1张

使用Jedis连接Redis集群
  • pom文件
<dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!-- lombok组件,需要你的IDE安装lombok插件,
            通过使用对应的注解,
            可以在编译源码的时候生成对应的方法,
            在这个例子中,
            @Data注解会在RedisConfig类中提供所有属性的getter和setter方法 -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
       <!--只有2.9.0才有密码设置-->
       <dependency>
           <groupId>redis.clients</groupId>
           <artifactId>jedis</artifactId>
           <version>2.9.0</version>
       </dependency>
       <!--使用commons-pool2连接池-->
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-pool2</artifactId>
       </dependency>
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
       </dependency>
  • application文件
spring:
  application:
    name: spring-boot-redis
  redis:
    #集群配置
    config:
      clusterNodes:
         - xx.xx.xxx.xxx:7001
         - xx.xx.xxx.xxx:7002
         - xx.xx.xxx.xxx:7003
         - xx.xx.xxx.xxx:7004
         - xx.xx.xxx.xxx:7005
         - xx.xx.xxx.xxx:7006
      connectionTimeout: 60000
      soTimeout: 3000
      maxAttempts: 1000
      password: 123456
  • redis属性配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;

/**
 * @author: caoweixiong
 * @date: 2020/04/29
 * @description:
 */ 
@Data
@Component
@ConfigurationProperties(prefix = "spring.redis.config")
public class RedisConfig{

    /**
     * 集群节点
     */
    private List<String> clusterNodes;

    /**
     * 密码
     */
    private String password;

    /**
     * 连接超时时间
     */
    private int connectionTimeout;

    /**
     * 读取数据超时时间
     */
    private int soTimeout;

    /**
     * 最大尝试次数
     */
    private int maxAttempts;

}
  • (JedisCluster属性配置、JedisPoolConfig属性配置)类
import com.asiainfo.redis.utils.JedisClusterUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

/**
 * @author: caoweixiong
 * @date: 2019/04/29
 * @description:
 */
@Configuration
public class JedisClusterConfig {

    @Autowired
    private RedisConfig redisConfig;

    private static Logger logger = LoggerFactory.getLogger(JedisClusterUtil.class);

    // 使用单例模式
    private static JedisCluster jedisCluster = null;

    @Bean
    public synchronized JedisCluster getJedisCluster() {
        try {
            logger.info(" >>>>>>> REDIS CLUSTER连接池,开始启动 >>>>>>> ");
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setTestOnBorrow(true);
            jedisPoolConfig.setTestOnReturn(true);
            jedisPoolConfig.setTestOnCreate(true);
            jedisPoolConfig.setTestWhileIdle(true);
            jedisPoolConfig.setMaxTotal(300);
            jedisPoolConfig.setMinIdle(5);
            //一定要设置不然会一直等待获取连接导致线程阻塞
            jedisPoolConfig.setMaxWaitMillis(6000);
            //获得节点配置信息
            Set<HostAndPort> nodes = new HashSet<>();
            if (redisConfig.getClusterNodes() != null) {
                for (String ipAndPort : redisConfig.getClusterNodes()) {
                    String[] ipOrPort = ipAndPort.split(":");
                    HostAndPort hostAndPort = new HostAndPort(ipOrPort[0], Integer.parseInt(ipOrPort[1]));
                    nodes.add(hostAndPort);
                }
            }
            //初始化 只有当jedisCluster为空时才实例化
            if (jedisCluster == null&&nodes.size() > 0)  {
                //redis有密码,配置JedisCluster
                if (redisConfig.getPassword() != null) {
                    jedisCluster = new JedisCluster(nodes, redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getMaxAttempts(), redisConfig.getPassword(), jedisPoolConfig);
                }
                //redis无密码,配置JedisCluster
                else {
                    jedisCluster = new JedisCluster(nodes, redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getMaxAttempts(), jedisPoolConfig);
                }
                logger.info(" >>>>>>> REDIS CLUSTER 连接池,启动成功 >>>>>> ");
            } else {
                logger.warn("{} redis 连接异常", nodes);
            }
        } catch (Exception e) {
            logger.error(">>>>>> REDIS CLUSTER 连接池,初始化失败 >>>>>> ", e);
            e.printStackTrace();
        }
        return jedisCluster;
    }
}
  • redis集群工具类
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisCluster;

import java.io.Serializable;

/**
 * @author: caoweixiong
 * @date: 2020/04/29
 * @description: redis集群工具类
 */
@Component
public class JedisClusterUtil implements Serializable {

    @Autowired
    private JedisCluster jedisCluster;

    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger(JedisClusterUtil.class);

    /**
     *
     * @param key   缓存key
     * @param value 缓存value
     */
    public void set(String key, String value) {
        jedisCluster.set(key, value);
        LOGGER.debug("JedisClusterUtil:set cache key={},value={}", key, value);
    }

    /**
     * 设置缓存对象
     *
     * @param key 缓存key
     * @param obj 缓存value
     */
    public <T> void setObject(String key, T obj, int expireTime) {
        jedisCluster.setex(key, expireTime, JSON.toJSONString(obj));
    }

    /**
     * 获取指定key的缓存
     *
     * @param key---JSON.parseObject(value, User.class);
     */
    public String getObject(String key) {
        return jedisCluster.get(key);
    }

    /**
     * 判断当前key值 是否存在
     *
     * @param key
     */
    public boolean hasKey(String key) {
        return jedisCluster.exists(key);
    }


    /**
     * 设置缓存,并且自己指定过期时间
     *
     * @param key
     * @param value
     * @param expireTime 过期时间
     */
    public void setWithExpireTime(String key, String value, int expireTime) {
        jedisCluster.setex(key, expireTime, value);
        LOGGER.debug("JedisClusterUtil:setWithExpireTime cache key={},value={},expireTime={}", key, value, expireTime);
    }


    /**
     * 获取指定key的缓存
     *
     * @param key
     */
    public String get(String key) {
        String value = jedisCluster.get(key);
        LOGGER.debug("JedisClusterUtil:get cache key={},value={}", key, value);
        return value;
    }

    /**
     * 删除指定key的缓存
     *
     * @param key
     */
    public void delete(String key) {
        jedisCluster.del(key);
        LOGGER.debug("JedisClusterUtil:delete cache key={}", key);
    }
}
  • redis集群测试类
import com.asiainfo.redis.po.TestMan;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;

import java.util.List;

import static org.junit.Assert.*;

/**
 * @author: caoweixiong
 * @date: 2020/04/29
 * @description: redis集群测试类
 */

@RunWith(SpringRunner.class)
@SpringBootTest
public class JedisClusterUtilTest {

    @Autowired
    JedisClusterUtil jedisClusterUtil;

    @Test
    public void set() {
        jedisClusterUtil.set("name", "jedis");
        Assert.assertEquals("jedis",jedisClusterUtil.get("name"));
    }

    @Test
    public void get() {
    }

    @Test
    public void setObject() {
        TestMan man = new TestMan();
        man.setId(10087L);
        man.setAge(35);
        man.setPassword("********");
        man.setSex(0);
        man.setUsername("cwx");
        jedisClusterUtil.setObject("10087L", man, 200);
        Assert.assertNotNull(jedisClusterUtil.getObject("10087L"));
    }

    @Test
    public void getObject() {
        System.out.println(jedisClusterUtil.getObject("10087L"));
    }

    @Test
    public void hasKey() {
    }

    @Test
    public void setWithExpireTime() {
    }

    @Test
    public void delete() {
        jedisClusterUtil.delete("10087L");
        Assert.assertEquals(null, jedisClusterUtil.get("10087L"));
    }
}

免责声明:文章转载自《Redis——Springboot集成Redis集群》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇C# 禁止windows程序重复运行的两种基本方法js大文件上传解决方案支持分片断点上传下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux自带神器logrotate详解

Linux自带神器logrotate详解 散尽浮华 运维 3天前   作者:散尽浮华 链接:https://www.cnblogs.com/kevingrace/p/6307298.html 对于 Linux 系统安全来说,日志文件是极其重要的工具。不知为何,我发现很多运维同学的服务器上都运行着一些诸如每天切分 Nginx日志之类的 CRON 脚本...

springboot 使用webflux响应式开发教程(二)

本篇是对springboot 使用webflux响应式开发教程(一)的进一步学习。 分三个部分: 数据库操作webservicewebsocket 创建项目,artifactId = trading-service,groupId=io.spring.workshop。选择Reactive Web , Devtools, Thymeleaf , React...

EncryptHelper加密对象-工具类

usingSystem; usingSystem.IO; usingSystem.Security.Cryptography; usingSystem.Text; usingSystem.Web.Security; namespaceCommon.Utility { /// <summary> ///Author...

MySQL8.0官方文档学习

InnoDB架构 下面的架构里只挑选了部分内容进行学习 内存架构(In-Memory Structures) Buffer Pool Buffer Pool是内存中的一块区域,InnoDB访问表和索引的时候缓存这些数据。buffer pool使得经常使用的数据直接从内存读取,加快了数据处理。在专用的服务器上,会给buffer pool分配80%的物理内存...

NeatUpload 的使用

1 <httpModules> 2 <add name="UploadHttpModule" type="Brettle.Web.NeatUpload.UploadHttpModule, Brettle.Web.NeatUpload" /> 3 </httpModules> 来自 <http://www....

Thymeleaf【快速入门】

前言:突然发现自己给自己埋了一个大坑,毕设好难..每一个小点拎出来都能当一个小题目(手动摆手..),没办法自己选的含着泪也要把坑填完..先一点一点把需要补充的知识学完吧.. Thymeleaf介绍 稍微摘一摘【官网】上面的介绍吧(翻译是找到,有些增加的内容): 1.Thymeleaf is a modern server-side Java templ...