從 Spring Boot 2.x 開始 Lettuce 已取代 Jedis 成爲首選 Redis 的客戶端。固然 Spring Boot 2.x 仍然支持 Jedis,而且你能夠任意切換客戶端。java
Lettuce 是一個可伸縮的線程安全的 Redis 客戶端,支持同步、異步和響應式模式。多個線程能夠共享一個鏈接實例,而沒必要擔憂多線程併發問題。它基於優秀 Netty NIO 框架構建,支持 Redis 的高級功能,如 Sentinel、集羣、流水線、自動從新鏈接和 Redis 數據模型redis
Jedis 在實現上是直接鏈接的 redis server,若是在多線程環境下是非線程安全的,這個時候只有使用鏈接池,爲每一個 Jedis 實例增長物理鏈接。spring
Lettuce 的鏈接是基於 Netty 的,鏈接實例 (StatefulRedisConnection) 能夠在多個線程間併發訪問,應爲 StatefulRedisConnection 是線程安全的,因此一個鏈接實例 (StatefulRedisConnection) 就能夠知足多線程環境下的併發訪問,固然這個也是可伸縮的設計,一個鏈接實例不夠的狀況也能夠按需增長鏈接實例。 數據庫
通常須要4步apache
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 緩存鏈接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
複製代碼
# redis 服務端相關配置
# 服務器地址
spring.redis.host=localhost
# 端口號
spring.redis.port=6379
# 密碼,默認爲 null
spring.redis.password=
# 使用的數據庫,默認選擇下標爲0的數據庫
spring.redis.database=0
# 客戶端超時時間,默認是2000ms
spring.redis.timeout=2000ms
## jedis 客戶端配置(從 Spring Boot 2.x 開始,再也不推薦使用 jedis 客戶端)
## 創建鏈接最大等待時間,默認1ms,超出該時間會拋異常。設爲-1表示無限等待,直到分配成功。
#spring.redis.jedis.pool.max-wait=1ms
## 最大連鏈接數,默認爲8,負值表示沒有限制
#spring.redis.jedis.pool.max-active=8
## 最大空閒鏈接數,默認8。負值表示沒有限制
#spring.redis.jedis.pool.max-idle=8
## 最小空閒鏈接數,默認0。
#spring.redis.jedis.pool.min-idle=0
# lettuce 客戶端配置(從 Spring Boot 2.x 開始,推薦使用 lettuce 客戶端)
# 創建鏈接最大等待時間,默認1ms,超出該時間會拋異常。設爲-1表示無限等待,直到分配成功。
spring.redis.lettuce.pool.max-wait=1ms
# 最大連鏈接數,默認爲8,負值表示沒有限制
spring.redis.lettuce.pool.max-active=8
# 最大空閒鏈接數,默認8。負值表示沒有限制
spring.redis.lettuce.pool.max-idle=8
# 最小空閒鏈接數,默認0。
spring.redis.lettuce.pool.min-idle=0
# 設置關閉鏈接的超時時間
spring.redis.lettuce.shutdown-timeout=100ms
複製代碼
RedisTemplate 是 spring 爲咱們提供的 redis 操做類,經過它咱們能夠完成大部分 redis 操做。json
只要咱們引入了 redis 依賴,並將 redis 的鏈接信息配置正確,springboot 就會根據咱們的配置會給咱們生成默認 RedisTemplate。緩存
可是默認生成的 RedisTemplate 有兩個地方不是很符合平常開發中的使用習慣安全
RedisTemplate<K, V>
接收的key
和value
爲泛型,常常須要類型轉換,直接使用不是很方便對於第一個問題,通常習慣將 RedisTemplate<K, V>
改成 RedisTemplate<String, Object>
,即接收的 key
爲 String 類型,接收的 value
爲 Object 類型 對於第二個問題,通常會把數據序列化爲 json 格式,而後存儲到 redis 中,序列化成 json 格式還有一個好處就是跨語言,其餘語言也能夠讀取你存儲在 redis 中的內容springboot
爲了實現上面兩個目的,咱們須要自定義本身的 RedisTemplate。bash
以下,建立一個 config 類,在裏面配置 自定義的 RedisTemplate
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
// 控制配置類的加載順序,先加載 RedisAutoConfiguration.class 再加載該類,這樣才能覆蓋默認的 RedisTemplate
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
/**
* 自定義 redisTemplate (方法名必定要叫 redisTemplate 由於 @Bean 是根據方法名配置這個bean的name的)
* 默認的 RedisTemplate<K,V> 爲泛型,使用時不太方便,自定義爲 <String, Object>
* 默認序列化方式爲 JdkSerializationRedisSerializer 序列化後的內容不方便閱讀,改成序列化成 json
*
* @param redisConnectionFactory
* @return
*/
@Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 配置 json 序列化器 - Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSerializer.setObjectMapper(objectMapper);
// 建立並配置自定義 RedisTemplateRedisOperator
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 將 key 序列化成字符串
template.setKeySerializer(new StringRedisSerializer());
// 將 hash 的 key 序列化成字符串
template.setHashKeySerializer(new StringRedisSerializer());
// 將 value 序列化成 json
template.setValueSerializer(jacksonSerializer);
// 將 hash 的 value 序列化成 json
template.setHashValueSerializer(jacksonSerializer);
template.afterPropertiesSet();
return template;
}
}
複製代碼
雖然 RedisTemplate 已經對 redis 的操做進行了必定程度的封裝,可是直接使用仍是有些不方便,實際開發中,通常會對 RedisTemplate 作近一步封裝,造成一個簡單、方便使用的Redis 操做類。
固然你也能夠選擇不封裝,看我的喜愛。
具體的封裝類就不展現了,每一個人都有本身的封裝方式,沒有統一的標準。
Spring Cache 是 Spring 爲緩存場景提供的一套解決方案。經過使用 @CachePut
、@CacheEvict
、@Cacheable
等註解實現對緩存的,存儲、查詢、刪除等操做
當咱們引入了 spring-boot-starter-data-redis
後,只要在帶有@Configuration
類上使用 @EnableCaching
註解 Spring Cache 就會被「激活」。
Spring Cache 會爲咱們配置默認的緩存管理器和key生成器,可是緩存管理器對緩存的序列化和key生成器生成的key,不易閱讀。建議自定義緩存管理器和key生成器
若是用不上 Spring Cache ,能夠不用管。
注意:Spring Cache 並非只能使用 Redis 做爲緩存容器,其餘例如 MemCache 等緩存中間件,都支持。
## spring cache 配置
# 使用的緩存的類型
spring.cache.type=redis
# 經過 spring cache 註解添加的緩存 的到期時間,單位秒(這是一個自定義屬性)
cache.expireTime=60
複製代碼
最重要的就是指定使用的緩存的類型
另外是一個自定義的變量,後面配置緩存管理器會用到
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
import java.time.Duration;
@Configuration
// 開啓 Spring Cache
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
@Value("${cache.expireTime}")
// 緩存超時時間
private int cacheExpireTime;
/**
* 配置@Cacheable、@CacheEvict等註解在沒有指定Key的狀況下,key生成策略
* 該配置做用於緩存管理器管理的全部緩存
* 最終生成的key 爲 cache類註解指定的cacheNames::類名:方法名#參數值1,參數值2...
*
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(":");
sb.append(method.getName());
sb.append("#");
for (Object obj : params) {
sb.append(obj.toString());
sb.append(",");
}
return sb.substring(0, sb.length() - 1);
}
};
}
/**
* 配置緩存管理器
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 配置 json 序列化器 - Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSerializer.setObjectMapper(objectMapper);
//關鍵點,spring cache 的註解使用的序列化都從這來,沒有這個配置的話使用的jdk本身的序列化,實際上不影響使用,只是打印出來不適合人眼識別
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
// 將 key 序列化成字符串
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 將 value 序列化成 json
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSerializer))//value序列化方式
// 設置緩存過時時間,單位秒
.entryTtl(Duration.ofSeconds(cacheExpireTime))
// 不緩存空值
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(cacheConfig)
.build();
}
}
複製代碼
網上 Spring Boot 集成 redis 的教程大多都是,將 redis 和 spring cache 一塊配置,很容易讓人產生誤解。
其實 redis 和 spring cache 是兩個不一樣的東西,因此,上面的教程我特地分爲了兩個配置文件。
你能夠只使用 redis 而不使用 spring cache,也能夠反過來。
那爲何二者常常放在一塊兒去討論呢?
緣由在於二者也有必定的聯繫
站在 reids 的角度看,spring cache 提供了一種便捷的操做 reids 的途徑,爲緩存場景提供了優秀的解決方案。
站在 spring cache 的角度看, reids 提供了一種緩存容器,能夠把緩存放在 reids 中。
緩存管理器對 reids 的操做也是經過 redisTemplate 實現的。