前言
在 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()); } }
-
执行结果如下:
使用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")); } }