1. 程式人生 > 程式設計 >Spring Boot實戰(六):Spring Boot 2.x整合Redis

Spring Boot實戰(六):Spring Boot 2.x整合Redis

最近在學習Spring Boot 2.x整合Redis,在這裡和大家分享一下,希望對大家有幫助。

Redis是什麼

Redis 是開源免費高效能的key-value資料庫。有以下的優勢(源於Redis官網):

  • 效能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。

  • 豐富的資料型別 – Redis支援二進位制案例的 Strings,Lists,Hashes,Sets 及 Ordered Sets 資料型別操作。

  • 原子 – Redis的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支援事務,即原子性,通過MULTI和EXEC指令包起來。

  • 豐富的特性 – Redis還支援 publish/subscribe,通知,key 過期等等特性。

如果想了解更多關於Redis是什麼,可以參考:Redis初識

為什麼要使用Redis

為什麼要使用Redis?21世紀以來資訊科技的飛速發展,我們已經迎來了大資料時代,資料較之前爆炸增長,併發訪問量激增;由之帶來的就是系統設計由單體架構向分散式架構的轉變和演進。對於快取這個問題,如果系統是單體的系統,可以在單個的JVM裡面進行本地快取,例如可以用HashMap來快取一些資料。但是到了分散式系統裡面,一個大系統裡面有很多個子系統,那他們之間如何共享快取?這就需要Redis,用Redis來實現快取系統。都說“時勢造英雄”,其實我想說Redis的大規模使用也是這個道理,用Redis做快取系統從效能和併發兩方面考慮都是值得肯定的。

  1. 效能:如果碰到需要執行耗時特別久,且結果不頻繁變動的SQL,就特別適合將執行結果放入快取。這樣,後面的請求就去快取中讀取,使得請求能夠迅速響應。

  2. 併發: 在大併發的情況下,所有的請求直接訪問資料庫,資料庫會出現連線異常。這個時候,就需要使用redis做一個緩衝操作,讓請求先訪問到Redis,而不是直接訪問資料庫。

更多關於“為什麼要使用Redis”,部落格園【原創】分散式之redis複習精講 ,這篇博文關於Redis的講解我覺得超讚,謝謝作者的耐心分享。

Spring Boot 2.x如何整合Redis

我使用的Spring Boot版本是2.1.0,根據網上的一些舊的教程進行整合Redis 3.2的時候,會有許多地方有錯誤提示。這是因為Spring Boot 2.x做了一些修改,這些修改對使用而有沒有影響呢?我們改怎麼整合呢?下面就進入正式的整合過程。

1. pom.xnl引入依賴

這是之前的依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-redis</artifactId>
        </dependency>  
複製程式碼

現在會提示Project build error: 'dependencies.dependency.version' for org.springframework.boot:spring-boot-starter-redis:jar is missing. 提示spring-boot-starter-redis:jar找不到。

這是因為Spring Boot 1.4之後不再支援spring-boot-starter-redis,更換spring-boot-starter-data-redis之後就可以了。如果你的pom檔案報錯,請檢查是否將spring-boot-starter-redis 改成了spring-boot-starter-data-redis。

Spring Boot 2.x要使用下面的依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency> 
複製程式碼

2. application.properties新增配置檔案

這是之前版本的配置檔案:

# REDIS (RedisProperties)
# Redis資料庫索引(預設為0)
spring.redis.database=0  
# Redis伺服器地址
spring.redis.host=127.0.0.1
# Redis伺服器連線埠
spring.redis.port=6379  
# Redis伺服器連線密碼(預設為空)
spring.redis.password=  
# 連線池最大連線數(使用負值表示沒有限制)
spring.redis.pool.max-active=8  
# 連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1 
# 連線池中的最大空閒連線
spring.redis.pool.max-idle=8  
# 連線池中的最小空閒連線
spring.redis.pool.min-idle=0  
# 連線超時時間(毫秒)
spring.redis.timeout=0  
複製程式碼

如果Spring Boot 2.x這麼配置,有錯誤提示 Property 'spring.redis.pool.max-active' is Deprecated: Use 'spring.redis.jedis.pool.max-idle' instead. 'spring.redis.pool.max-active'已經被棄用了,推薦使用'spring.redis.jedis.pool.max-idle'來代替。

這是因為在2.x中配置redis的連線池資訊時,不再使用spring.redis.pool的屬性,而是直接使用redis的lettuce或jedis客戶端來配置。現在的配置如下:

# REDIS (RedisProperties)
# Redis資料庫索引(預設為0)
spring.redis.database=0  
# Redis伺服器地址
spring.redis.host=127.0.0.1
# Redis伺服器連線埠
spring.redis.port=6379  
# Redis伺服器連線密碼(預設為空)
spring.redis.password=  
# 連線池最大連線數(使用負值表示沒有限制)
spring.redis.jedis.pool.max-active=8  
# 連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.jedis.pool.max-wait=-1
# 連線池中的最大空閒連線
spring.redis.jedis.pool.max-idle=8  
# 連線池中的最小空閒連線
spring.redis.jedis.pool.min-idle=0  
# 連線超時時間(毫秒)
spring.redis.timeout=0  
複製程式碼

3. 配置CacheManager

這是之前的RedisConfig配置類:

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public CacheManager cacheManager(RedisTemplate<?,?>  redisTemplate) {
        CacheManager cacheManager = new  RedisCacheManager(redisTemplate);
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String,String>  redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String,String> redisTemplate = new  RedisTemplate<String,String>();
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}
複製程式碼

現在這麼寫會有報錯The constructor RedisCacheManager(RedisTemplate<capture#1-of ?,capture#2-of ?>) is undefined。

這是因為Spring Boot 2.x版本刪除了RedisCacheManager這個構造器, 也不可以通過之前的setDefaultExpiration方法設定預設的快取過期時間等。

那麼要怎樣構造一個 RedisCacheManager?看看官方檔案中怎麼說?檔案地址:docs.spring.io/spring-data…

官方檔案5.13.1. Support for the Spring Cache Abstraction(對Spring Cache Abstraction的支援)是關於怎麼配置快取的說明,我嘗試著翻譯了一下(藍色部分),英文水平有限,各位輕噴。

Spring Redis provides an implementation for the Spring cache abstraction through the org.springframework.data.redis.cache package. To use Redis as a backing implementation,add RedisCacheManager to your configuration,as follows:

Spring Redis在 org.springframework.data.redis.cache 包中為Spring快取抽象提供了一個實現方案。要使用Redis作為支援實現,需要將RedisCacheManager新增到配置中,如下所示:

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    return RedisCacheManager.create(connectionFactory);
}
複製程式碼

RedisCacheManager behavior can be configured with RedisCacheManagerBuilder,letting you set the default RedisCacheConfiguration,transaction behavior,and predefined caches.

RedisCacheManager可以用 RedisCacheManagerBuilder 進行配置,允許自定義設定預設的RedisCacheConfiguration、事務行為和預定義的快取。

RedisCacheManager cm = RedisCacheManager.builder(connectionFactory)
    .cacheDefaults(defaultCacheConfig())
    .initialCacheConfigurations(singletonMap("predefined",defaultCacheConfig().disableCachingNullValues()))
    .transactionAware()
    .build();
複製程式碼

As shown in the preceding example,RedisCacheManager allows definition of configurations on a per-cache basis.

正如上面的例子所示,RedisCacheManager 允許基於每個快取進行配置。

The behavior of RedisCache created with RedisCacheManager is defined with RedisCacheConfiguration. The configuration lets you set key expiration times,prefixes,and RedisSerializer implementations for converting to and from the binary storage format,as shown in the following example:

RedisCacheManager建立RedisCache的行為被定義為RedisCacheConfiguration。該配置允許設定鍵值過期時間、字首和RedisSerializer實現等屬性,以便與二進位制儲存格式進行轉換,如下所示:

RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(1))
    .disableCachingNullValues();
複製程式碼

RedisCacheManager defaults to a lock-free RedisCacheWriter for reading and writing binary values. Lock-free caching improves throughput. The lack of entry locking can lead to overlapping,non-atomic commands for the putIfAbsent and clean methods,as those require multiple commands to be sent to Redis. The locking counterpart prevents command overlap by setting an explicit lock key and checking against presence of this key,which leads to additional requests and potential command wait times.

RedisCacheManager預設是無鎖的,用於讀寫二進位制值的RedisCacheWriter。無鎖快取可提高吞吐量。缺少鎖定可能導致putIfAbsent和clean方法的非原子命令重疊,因為這些方法需要向Redis傳送多條命令。鎖對應程式通過設定顯式鎖定的key並檢查是否存在該key來防止命令重疊,這會導致額外的請求和潛在的命令等待時間。

It is possible to opt in to the locking behavior as follows:

也可以選擇鎖定行為,如下所示:

RedisCacheManager cm = RedisCacheManager.build(RedisCacheWriter.lockingRedisCacheWriter())
    .cacheDefaults(defaultCacheConfig())
    ...
複製程式碼

By default,any key for a cache entry gets prefixed with the actual cache name followed by two colons. This behavior can be changed to a static as well as a computed prefix.

預設情況下,快取條目的任何key目以實際快取名稱為字首,後跟兩個冒號。此行為可以更改為靜態字首和計算字首。

The following example shows how to set a static prefix:

以下示例顯示如何設定靜態字首:

// static key prefix
RedisCacheConfiguration.defaultCacheConfig().prefixKeysWith("( ͡° ᴥ ͡°)");

The following example shows how to set a computed prefix:

// computed key prefix
RedisCacheConfiguration.defaultCacheConfig().computePrefixWith(cacheName -> "¯\_(ツ)_/¯" + cacheName);
複製程式碼

The following table lists the default settings for RedisCacheManager:

下表列出了RedisCacheManager的預設設定:

Setting Value
Cache Writer Non-locking
Cache Configuration RedisCacheConfiguration#defaultConfiguration
Initial Caches None
Trasaction Aware No

The following table lists the default settings for RedisCacheConfiguration:

下表列出了RedisCacheConfiguration的預設設定:

Key Expiration None
Cache null Yes
Prefix Keys Yes
Default Prefix The actual cache name
Key Serializer StringRedisSerializer
Value Serializer JdkSerializationRedisSerializer
Conversion Service DefaultFormattingConversionService with default cache key converters

對於Spring Boot 2.x,這裡我使用RedisCacheConfiguration簡單配置一下快取時間,完成RedisCacheManager的配置,程式碼如下:

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory  factory) {
        RedisCacheConfiguration config =  RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(1));
        RedisCacheManager cacheManager =  RedisCacheManager.builder(factory).cacheDefaults(config).build();
        return cacheManager;
    }
}
複製程式碼

4. Service層

使用快取的情況一般是這樣的:第一次訪問的時候先從資料庫讀取資料,然後將資料寫入到快取,再次訪問同一內容的時候就從快取中讀取,如果快取中沒有則從資料庫中讀取。

新增快取邏輯的時候,從資料庫中將內容讀取出來之後,先set入快取,然後再從快取中新增讀取行為,如果快取為空則從資料庫中進行讀取。

在這裡僅建立一個簡單的RedisService,來進行存取快取資料。

@Service
public class RedisService {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    public void set(String key,Object value) {
        //更改在redis裡面檢視key編碼問題
        RedisSerializer redisSerializer = new  StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        ValueOperations<String,Object> vo =  redisTemplate.opsForValue();
        vo.set(key,value);
    }

    public Object get(String key) {
        ValueOperations<String,Object> vo =  redisTemplate.opsForValue();
        return vo.get(key);
    }
}
複製程式碼

5. Model層

實體類沒有修改,和之前文章裡面用的一樣:

@Entity
@Table(name = "user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;
    @Column(name = "username")
    private String userName;
    @Column(name = "password")
    private String passWord;

    public User() {
        super();
    }

    public User(String userName,String passWord) {
        super();
        this.userName = userName;
        this.passWord = passWord;
    }

    public User(Long id,String userName,String passWord) {
        super();
        this.id = id;
        this.userName = userName;
        this.passWord = passWord;
    }
    //省略getter和setter方法
    
}
複製程式碼

6. Controller層

在user.Controller新增兩個測試方法,一個寫入Redis,一個從Redis讀取:

@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RedisService redisService;

    @RequestMapping("/saveUser")
    public String saveUser(Long id,String  passWord) {
        User user = new User(id,userName,passWord);
        redisService.set(id + "",user);
        return "success";
    }

    @RequestMapping("/getUserById")
    public User getUserById(Long id) {
        User res = (User) redisService.get(id + "");
        return res;
    }
}
複製程式碼

7. 測試

使用Postman進行測試,訪問http://localhost:8080//user/saveUser?id=12&userName=Howard&passWord=magician,添加了一個User。

看看Redis資料庫,使用get key檢視,得到一個物件:

image

http://localhost:8080//user/getUserById?id=12,通過id獲取User。

image

這是過濾器和攔截器的日誌資訊,可以看到沒有進行MySQL資料庫的操作,直接從快取中讀取,說明Redis配置生效了:

image

如果訪問之前的介面,是有操作資料庫的:

image

總結

以上就是Spring Boot 2.x整合Redis的全過程,和Spring Boot之前的版本在使用上有些細微的差別。

由於水平有限,文中或多或少有欠妥之處,還望各位大佬指正!