前言
在 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")); } }
