SpringBoot整合Redis做緩存,實(shí)戰(zhàn)分享

我們都知道,把首頁數(shù)據(jù)放到Redis里,能夠加快首頁數(shù)據(jù)的訪問速度。但是我們要如何準(zhǔn)確又快速的將 Redis 整合到自己的 SpringBoot2.x 項目中呢?今天阿粉就帶大家爬一爬其中的門門道道。
Redis 介紹
Redis 使用了浪費(fèi)流量的文本協(xié)議,但因為它數(shù)據(jù)存儲在內(nèi)存中的,相對而言,依然可以取得極高的訪問性能。并且 Redis 是線程安全的。
RESP 就是 Redis 序列化協(xié)議的簡稱。它是一種直觀的文本協(xié)議,優(yōu)勢在于實(shí)現(xiàn)異常簡單,解析性能極好。
Redis 協(xié)議里面雖然有大量冗余的回車換行符,但是這不影響它成為技術(shù)領(lǐng)域非常受歡迎的一個文本協(xié)議。在技術(shù)領(lǐng)域,性能并不總是一切,還有簡單性、易理解性和易實(shí)現(xiàn)性,這些都需要進(jìn)行適當(dāng)權(quán)衡。
Redis 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
1、字符串:(緩存)
key:value
value 可以是對象轉(zhuǎn)換成的 JSON 字符串,也可以是對象序列化后的二進(jìn)制字符串
2、列表:(異步隊列) 類似linkedlist
右邊進(jìn)左邊出:隊列 右邊進(jìn)右邊出:棧
3、字典(哈希) 類似hashmap:數(shù)組+鏈表
不過rehash是漸進(jìn)式hash策略
4、集合:(去重)
無序 set:類似hashset 有序 zset:類似SortedSet和HashMap的結(jié)合體,內(nèi)部實(shí)現(xiàn)是跳躍列表
Lettuce
隨著 Spring Boot2.x 的到來,支持的組件越來越豐富,也越來越成熟,其中對 Redis 的支持不僅僅是豐富了它的API,更是替換掉底層 Jedis 的依賴,取而代之換成了 Lettuce。
雖然 Lettuce 和 Jedis 的都是連接 Redis Server 的客戶端程序,但是 Jedis 在實(shí)現(xiàn)上是直連 redis server,多線程環(huán)境下非線程安全,除非使用連接池,為每個Jedis實(shí)例增加物理連接。而 Lettuce 基于 Netty 的連接實(shí)例(StatefulRedisConnection),可以在多個線程間并發(fā)訪問,且線程安全,滿足多線程環(huán)境下的并發(fā)訪問,同時它是可伸縮的設(shè)計,一個連接實(shí)例不夠的情況也可以按需增加連接實(shí)例。
Lettuce是可擴(kuò)展的Redis客戶端,用于構(gòu)建無阻塞的Reactive應(yīng)用程序.
Luttuce官網(wǎng):https://lettuce.io/

谷歌翻譯后的頁面是:

原來這玩意兒叫生菜,你別說,看著圖標(biāo)還真有點(diǎn)像。

實(shí)操
項目中使用的 SpringBoot2.x 實(shí)現(xiàn),如果之前是 SpringBoot1.x 則需要注意,底層已經(jīng)由 Jedis 升級成了 Lettuce 。
3.1、引入依賴
除去 SpringBoot 項目需要的 jar 包外,另外還需要引入 redis 相關(guān)的依賴:
????org.springframework.boot
????spring-boot-starter-data-redis
????org.apache.commons
????commons-pool2
3.2、application.yml 配置文件
此處用到的 application.yml 文件,配置如下:
spring:
??redis:
????# Redis默認(rèn)情況下有16個分片,這里配置具體使用的分片。默認(rèn)是索引為0的分片
????database:?1
????#?Redis服務(wù)器地址
????host:?127.0.0.1
????#?Redis服務(wù)器連接端口
????port:?6379
????#?Redis服務(wù)器連接密碼(默認(rèn)為空)
????password:?mmzsblog
????#?連接超時時間(毫秒)
????timeout:?2000s
????#?配置文件中添加?lettuce.pool?相關(guān)配置,則會使用到lettuce連接池
????lettuce:
??????pool:
????????#?連接池最大阻塞等待時間(使用負(fù)值表示沒有限制)?默認(rèn)?-1
????????max-wait:?60s
????????#?連接池中的最大空閑連接?默認(rèn)?8
????????max-idle:?10
????????#?連接池中的最小空閑連接?默認(rèn)?0
????????min-idle:?10
????????#?連接池最大連接數(shù)(使用負(fù)值表示沒有限制)?默認(rèn)?8
????????max-activ:?8
如果項目是由 SpringBoot1.x 升級到 SpringBoot2.x 的,要沿用 jedis 連接池配置時會用到配置 jedis 相關(guān)的屬性:
????#?配置文件中添加?jedis.pool?相關(guān)配置,則會使用到?jedis?連接池
????jedis:
??????pool:
????????max-active:?10
????????max-idle:?8
????????min-idle:?0
????????max-wait:?60s
并且引用的 jar 包也需要調(diào)整:
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-data-redisartifactId>
????<exclusions>
????????<exclusion>
????????????<groupId>io.lettucegroupId>
????????????<artifactId>lettuce-coreartifactId>
????????exclusion>
????exclusions>
dependency>
<dependency>
????<groupId>redis.clientsgroupId>
????<artifactId>jedisartifactId>
dependency>
另外,這里再貼一下 Spring Boot 關(guān)于 RedisProperties 的所有配置項
# REDIS RedisProperties
spring.redis.cluster.max-redirects= # Maximum number of redirects to follow when executing commands across the cluster.
spring.redis.cluster.nodes= # Comma-separated list of "host:port" pairs to bootstrap from.
spring.redis.database=0 # Database index used by the connection factory.
spring.redis.url= # Connection URL. Overrides host, port, and password. User is ignored. Example: redis://user:[email protected]:6379
spring.redis.host=localhost # Redis server host.
spring.redis.jedis.pool.max-active=8 # Maximum number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.jedis.pool.max-idle=8 # Maximum number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.jedis.pool.max-wait=-1ms # Maximum amount of time a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.jedis.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
spring.redis.lettuce.pool.max-active=8 # Maximum number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.lettuce.pool.max-idle=8 # Maximum number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.lettuce.pool.max-wait=-1ms # Maximum amount of time a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.lettuce.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
spring.redis.lettuce.shutdown-timeout=100ms # Shutdown timeout.
spring.redis.password= # Login password of the redis server.
spring.redis.port=6379 # Redis server port.
spring.redis.sentinel.master= # Name of the Redis server.
spring.redis.sentinel.nodes= # Comma-separated list of "host:port" pairs.
spring.redis.ssl=false # Whether to enable SSL support.
spring.redis.timeout= # Connection timeout.
3.3、自定義一個 RedisTemplate
這個看你自己,不自定義也不影響使用,只是說可能不那么順手,所以阿粉習(xí)慣自定義一個。因為 Spring Boot 在 RedisAutoConfiguration 中默認(rèn)配置了 RedisTemplate、StringRedisTemplate兩個模板類,然而RedisTemplate并未指定key、value的序列化器。
@Configuration
public?class?RestTemplateConfig?{
????@Bean
????public?RedisTemplate?redisCacheTemplate(LettuceConnectionFactory?redisConnectionFactory)? {
????????RedisTemplate?template?=?new?RedisTemplate<>();
????????template.setKeySerializer(new?StringRedisSerializer());
????????template.setValueSerializer(new?GenericJackson2JsonRedisSerializer());
????????template.setConnectionFactory(redisConnectionFactory);
????????return?template;
????}
}
3.4、Person 實(shí)體類
聲明一個 Person 實(shí)體類:
/**
?*?@author?:created by mmzsblog
?*?@date?:created at 2020/06/23 16:41
?*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public?class?Person?implements?Serializable?{
????private?static?final?long?serialVersionUID?=?-8183942491930372236L;
????private?Long?userId;
????private?String?username;
????private?String?password;
}
3.5、測試
通過編寫一個 UserController 來進(jìn)行測試:
@RestController
public?class?UserController?{
????@Resource
????private?RedisTemplate?redisTemplate;
????@Resource
????private?StringRedisTemplate?stringRedisTemplate;
????@GetMapping("/set")
????public?String?set()?{
????????stringRedisTemplate.opsForValue().set("one",?"1");
????????
????????// redisTemplate 保存的是字節(jié)序列,因為 RestTemplateConfig 自定義的時候指定了 key 和 value 的序列化器。
????????redisTemplate.opsForValue().set("two",?"2");
????????redisTemplate.opsForValue().set("person",?new?Person(1L,?"luffy",?"123456789"));
????????//?測試線程安全
????????ExecutorService?executorService?=?Executors.newFixedThreadPool(1000);
????????IntStream.range(0,?1000).forEach(i?->?{
????????????executorService.execute(()?->?stringRedisTemplate.opsForValue().increment("num",?1));
????????});
????????return?"Ok!";
????}
????@GetMapping("/get")
????public?String?get()?{
????????String?one?=?stringRedisTemplate.opsForValue().get("one");
????????if?("1".equals(one))?{
????????????System.out.println("key:?one"?+?"?||?value:?"?+?one);
????????}
????????Object?two?=?redisTemplate.opsForValue().get("two");
????????if?("2".equals(two.toString()))?{
????????????System.out.println("key:?two"?+?"?||?value:?"?+?two);
????????}
????????Person?user?=?(Person)?redisTemplate.opsForValue().get("person");
????????if?("luffy".equals(user.getUsername()))?{
????????????System.out.println("key:?person"?+?"?||?value:?"?+?user);
????????}
????????return?"Ok!";
????}
}
用RedisDesktopManager工具查看,數(shù)據(jù)如下:
用 StringRedisTemplate 設(shè)置的鍵值是String類型的:

用 RedisTemplate 設(shè)置的鍵值是二進(jìn)制的字節(jié)流形式存儲的,從截圖中的 [Binary] 標(biāo)識符也能看出:

自定義的 RedisTemplate 和 StringRedisTemplate 并不會有什么沖突,想用 String 存儲還是二進(jìn)制的字節(jié)流形式存儲完全取決于你自己。
參考
https://lettuce.io/ Spring Boot官方文檔 91.4 《Redis深度歷險:核心原理和應(yīng)用實(shí)踐》 http://blog.battcn.com/2018/05/11/springboot/v2-nosql-redis/ https://www.jianshu.com/p/f7d11e7109b7
< END >
