SpringBoot整合Redis做緩存,實戰分享

每天早上七點三十,準時推送乾貨

我們都知道,把首頁數據放到Redis裏,能夠加快首頁數據的訪問速度。但是我們要如何準確又快速的將 Redis 整合到自己的 SpringBoot2.x 項目中呢?今天阿粉就帶大家爬一爬其中的門門道道。

Redis 介紹

Redis 使用了浪費流量的文本協議,但因爲它數據存儲在內存中的,相對而言,依然可以取得極高的訪問性能。並且 Redis 是線程安全的。

RESP 就是 Redis 序列化協議的簡稱。它是一種直觀的文本協議,優勢在於實現異常簡單,解析性能極好。

Redis 協議裏面雖然有大量冗餘的回車換行符,但是這不影響它成爲技術領域非常受歡迎的一個文本協議。在技術領域,性能並不總是一切,還有簡單性、易理解性和易實現性,這些都需要進行適當權衡。

Redis 基礎數據結構

1、字符串:(緩存)

  • key:value

value 可以是對象轉換成的 JSON 字符串,也可以是對象序列化後的二進制字符串

2、列表:(異步隊列) 類似linkedlist

  • 右邊進左邊出:隊列

  • 右邊進右邊出:棧

3、字典(哈希) 類似hashmap:數組+鏈表

不過rehash是漸進式hash策略

4、集合:(去重)

  • 無序 set:類似hashset

  • 有序 zset:類似SortedSet和HashMap的結合體,內部實現是跳躍列表

Lettuce

隨着 Spring Boot2.x 的到來,支持的組件越來越豐富,也越來越成熟,其中對 Redis 的支持不僅僅是豐富了它的API,更是替換掉底層 Jedis 的依賴,取而代之換成了 Lettuce。

雖然 Lettuce 和 Jedis 的都是連接 Redis Server 的客戶端程序,但是 Jedis 在實現上是直連 redis server,多線程環境下非線程安全,除非使用連接池,爲每個Jedis實例增加物理連接。而 Lettuce 基於 Netty 的連接實例(StatefulRedisConnection),可以在多個線程間併發訪問,且線程安全,滿足多線程環境下的併發訪問,同時它是可伸縮的設計,一個連接實例不夠的情況也可以按需增加連接實例。

Lettuce是可擴展的Redis客戶端,用於構建無阻塞的Reactive應用程序.

Luttuce官網:https://lettuce.io/

谷歌翻譯後的頁面是:

原來這玩意兒叫生菜,你別說,看着圖標還真有點像。


實操

項目中使用的 SpringBoot2.x 實現,如果之前是 SpringBoot1.x 則需要注意,底層已經由 Jedis 升級成了 Lettuce 。

3.1、引入依賴

除去 SpringBoot 項目需要的 jar 包外,另外還需要引入 redis 相關的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

3.2、application.yml 配置文件

此處用到的 application.yml 文件,配置如下:

spring:
  redis:
    # Redis默認情況下有16個分片,這裏配置具體使用的分片。默認是索引爲0的分片
    database: 1
    # Redis服務器地址
    host: 127.0.0.1
    # Redis服務器連接端口
    port: 6379
    # Redis服務器連接密碼(默認爲空)
    password: mmzsblog
    # 連接超時時間(毫秒)
    timeout: 2000s

    # 配置文件中添加 lettuce.pool 相關配置,則會使用到lettuce連接池
    lettuce:
      pool:
        # 連接池最大阻塞等待時間(使用負值表示沒有限制) 默認 -1
        max-wait: 60s
        # 連接池中的最大空閒連接 默認 8
        max-idle: 10
        # 連接池中的最小空閒連接 默認 0
        min-idle: 10
        # 連接池最大連接數(使用負值表示沒有限制) 默認 8
        max-activ: 8

如果項目是由 SpringBoot1.x 升級到 SpringBoot2.x 的,要沿用 jedis 連接池配置時會用到配置 jedis 相關的屬性:

    # 配置文件中添加 jedis.pool 相關配置,則會使用到 jedis 連接池
    jedis:
      pool:
        max-active: 10
        max-idle: 8
        min-idle: 0
        max-wait: 60s

並且引用的 jar 包也需要調整:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

另外,這裏再貼一下 Spring Boot 關於 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

這個看你自己,不自定義也不影響使用,只是說可能不那麼順手,所以阿粉習慣自定義一個。因爲 Spring BootRedisAutoConfiguration 中默認配置了 RedisTemplate<Object, Object>StringRedisTemplate兩個模板類,然而RedisTemplate<Object, Object>並未指定keyvalue的序列化器。

@Configuration
public class RestTemplateConfig {
    @Bean
    public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

3.4、Person 實體類

聲明一個 Person 實體類:

/**
 * @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 來進行測試:

@RestController
public class UserController {
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/set")
    public String set() {
        stringRedisTemplate.opsForValue().set("one", "1");
        
        // redisTemplate 保存的是字節序列,因爲 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工具查看,數據如下:

StringRedisTemplate 設置的鍵值是String類型的:

RedisTemplate 設置的鍵值是二進制的字節流形式存儲的,從截圖中的 [Binary] 標識符也能看出:

自定義的 RedisTemplateStringRedisTemplate 並不會有什麼衝突,想用 String 存儲還是二進制的字節流形式存儲完全取決於你自己。

參考

  • https://lettuce.io/

  • Spring Boot官方文檔 91.4

  • 《Redis深度歷險:核心原理和應用實踐》

  • http://blog.battcn.com/2018/05/11/springboot/v2-nosql-redis/

  • https://www.jianshu.com/p/f7d11e7109b7

最後說兩句(求關注)

最近大家應該發現微信公衆號信息流改版了吧,再也不是按照時間順序展示了。這就對阿粉這樣的堅持的原創小號主,可以說非常打擊,閱讀量直線下降,正反饋持續減弱。

所以看完文章,哥哥姐姐們給阿粉來個在看吧,讓阿粉擁有更加大的動力,寫出更好的文章,拒絕白嫖,來點正反饋唄~。

如果想在第一時間收到阿粉的文章,不被公號的信息流影響,那麼可以給Java極客技術設爲一個星標

最後感謝各位的閱讀,才疏學淺,難免存在紕漏,如果你發現錯誤的地方,由於本號沒有留言功能,還請你在後臺留言指出,我對其加以修改。

最後謝謝大家支持~

最最後,重要的事再說一篇~

快來關注我呀~
快來關注我呀~
快來關注我呀~

< END >

如果大家喜歡我們的文章,歡迎大家轉發,點擊在看讓更多的人看到。也歡迎大家熱愛技術和學習的朋友加入的我們的知識星球當中,我們共同成長,進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章