[Redis 客戶端整合] SpringBoot 整合 Lettuce

DavidH發表於2024-11-22

一、簡介

​ 在使用SpringBoot搭建微服務的時候, 很多時候需要用Redis來快取一些資料 , 儲存一些高頻率訪問的資料。SpringBoot整合Redis所常用的Redis常用客戶端有三個:

  • Jedis api
  • redisson
  • lettuce

本文采用lettuce來訪問Redis, 在Spring boot2之後, 對redis連線的支援, 預設就採用了lettuce

二、匯入依賴

        <!-- SpringData 用於簡化資料庫訪問-->
        <!--預設是lettuce客戶端-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- redis依賴commons-pool 這個依賴一定要新增 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

三、配置連線資訊

application.yml檔案中, 配置 Redis 連線資訊

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-active: 8 # 連線池最大連線數(使用負值表示沒有限制)
        max-idle: 8 # 連線池中的最大空閒連線
        min-idle: 0 # 連線池中的最小空閒連線
        max-wait: 1000 # 連線池最大阻塞等待時間(使用負值表示沒有限制)
      shutdown-timeout: 100 # 關閉超時時間

四、RedisConfig類介紹

快取配置類RedisConfig用於調優快取預設配置, 使RedisTemplate<String,Object>的型別相容性更高

redisTemplate()這個方法中, 用Jackson2JsonRedisSerializer更換掉了Redis預設的序列化方式 JdkSerializationRedisSerializer

spring-data-redis中序列化類有一下幾個:

  • GenericToStringSerializer : 可以將任何物件泛化為字串並序列化
  • Jackson2JsonRedisSerializer : 序列化 Object 物件為 Json 字串 (與JacksonJsonRedisSerializer相同)
  • JdkSerializationRedisSerializer : 序列化 Java 物件
  • StringRedisSerializer : 簡單的字串序列化

JdkSerializationRedisSerializer序列化 : 被序列化物件必須實現Serializable介面 , 被序列化除屬性內容還有其他內容 , 長度長且不易閱讀 , 預設就是採用這種序列化方式

儲存內容如下:

JdkSerializationRedisSerializer序列化結果.png

JdkSerializationRedisSerializer序列化 : 被序列化物件不需要實現Serializable介面 , 被序列化的結果清晰 , 內容易閱讀 , 且儲存位元組少 , 速度快

儲存內容如下:

Jackson2JsonRedisSerializer序列化結果.png

StringRedisSerializer序列化 : 一般如果key、value都是字串的話 , 用這個就可以了

五、編寫RedisConfig類

@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * 自定義快取key的生成策略,預設的生成策略是看不懂的(亂碼內容)
     * 透過Spring的依賴注入特性進行自定義的配置注入並且此類是一個配置類, 可以更多程度的自定義配置
     * @return
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append(target.getClass().getName());
                stringBuilder.append(method.getName());
                for (Object obj :
                        params) {
                    stringBuilder.append(obj.toString());
                }
                return stringBuilder.toString();
            }
        };
    }

    /**
     * 快取配置管理器
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory){
        //以鎖寫入的方式建立RedisCacheWriter物件
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
        //建立預設快取配置物件
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheManager cacheManager = new RedisCacheManager(writer,config);
        return cacheManager;
    }

    //@ConditionalOnMissingBean(name="redisTemplate") 如果沒有RedisTemplate的Bean則使用該註解定義的Bean
    @Bean
    public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //在使用註解@Bean返回RedisTemplate的時候,同時配置hashKey與hashValue的序列化方式
        //key採用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //value序列化方式採用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);

        //hash的key採用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //hash的value序列化方式採用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();

        return template;
    }
}

五、使用RedisTemplate

(1)注入 RedisTemplate 物件

在要使用 Redis模板 的類中注入 RedisTemplate 物件

@Autowired
private RedisTemplate<String,Object> redisTemplate;

(2)使用 RedisTemplate 物件

redisTemplate 類是對lettuce的進一步封裝 , 因此他的方法並不與Redis語法一一對應。

對於Redis中所有型別共有的命令, 可以使用redisTemplate.方法() 對Redis進行操作

對於Redis中特定型別的命令, 需要使用redisTemplate.opsFor型別().方法()對Redis進行操作

  • String 型別 : redisTemplate.opsForValue().方法()
  • Hash 型別 : redisTemplate.opsForHash().方法()
  • List 型別 : redisTemplate.opsForList().方法()
  • Set 型別 : redisTemplate.opsForSet().方法()
  • ZSet 型別 : redisTemplate.opsForZSet().方法()

(3) 使用 RedisTemplate 注入 ValueOperations

如果每次呼叫方法, 都用redisTemplate.opsFor型別().方法() , 在程式碼中會有很多重複的redisTemplate.opsFor型別()

為了解決這個問題 , 我們可以使用 RedisTemplate 注入 型別 + Operations

以String型別為例:

@Autowired
private RedisTemplate<String,Object> redisTemplate;

@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps; // 相當於 vOps = redisTemplate.opsForValue()

RedisTemplate 型別和 ValueOperations 型別並沒有任何的關係, 他之所以可以注入是因為Spring在注入時對其進行了型別轉換。若想要深入瞭解redisTemplate如何注入到ValueOperations, 可以檢視此部落格: redisTemplate如何注入到ValueOperations

(4) 例項 - RedisTemplate 操作 String 型別

@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps;

public String gerString(String key){
    String val = null;
    if(redisTemplate.hasKey(key)){
        val = (String) vOps.get(key);
        log.info("從Redis查詢");
    }else {
        val ="模擬MySql查詢";
        log.info("從MySql查詢");
        vOps.set(key,val);
    }
    return val;
}

(5) 例項 - RedisTemplate 操作 Hash 型別

User實體類

@Data
public class User implements Serializable {
    private String id;
    private String name;
    private Integer age;
}

查詢 User 方法

@Resource(name = "redisTemplate")
private HashOperations<String,String,User> hOps;

public User selectUserById(String id){
    if(hOps.hasKey("user", id)){
        log.info("查詢Redis");
        return (User) hOps.get("user",id);
    }else{
        User user = new User();
        user.setId(id);
        user.setName("王五");
        user.setAge(20);
        log.info("查詢Mysql資料庫");
        hOps.put("user",id,user);
        return user;
    }
}

相關文章