springboot系列文章之 整合redis 服務 (Lettuce & Jedis)

pjmike_pj發表於2018-09-18

原文部落格地址: pjmike的部落格

前言

在實際專案開發過程中,相信很多人都有用到過 redis 這個NoSQL,這篇文章就詳細講講springboot如何整合 redis

Redis 簡介

簡單介紹下Redis:

Redis是一個開源的使用 ANSI C語言編寫,支援網路,可基於記憶體也可持久化的日誌型,Key-Value資料庫,並提供了多種語言的 API ,相比 Memcached 它支援儲存的型別相對更多 (字元,雜湊,集合,有序集合,列表等),同時Redis是執行緒安全的。

Redis 連線池簡介

在後面 springboot 整合 redis 的時候會用到連線池,所以這裡先來介紹下 Redis中的連線池:

客戶端連線 Redis 使用的是 TCP協議,直連的方式每次需要建立 TCP連線,而連線池的方式是可以預先初始化好客戶端連線,所以每次只需要從 連線池借用即可,而借用和歸還操作是在本地進行的,只有少量的併發同步開銷,遠遠小於新建TCP連線的開銷。另外,直連的方式無法限制 redis客戶端物件的個數,在極端情況下可能會造成連線洩漏,而連線池的形式可以有效的保護和控制資源的使用。

下面以Jedis客戶端為例,再來總結下 客戶端直連方式和連線池方式的對比

優點 缺點
直連 簡單方便,適用於少量長期連線的場景 1. 存在每次新建/關閉TCP連線開銷 2. 資源無法控制,極端情況下出現連線洩漏 3. Jedis物件執行緒不安全(Lettuce物件是執行緒安全的)
連線池 1. 無需每次連線生成Jedis物件,降低開銷 2. 使用連線池的形式保護和控制資源的使用 相對於直連,使用更加麻煩,尤其在資源的管理上需要很多引數來保證,一旦規劃不合理也會出現問題

Jedis vs Lettuce

redis官方提供的java client有如圖所示幾種:

redis
比較突出的是 Lettuce 和 jedis。Lettuce 和 jedis 的都是連線 Redis Server的客戶端,Jedis 在實現上是直連 redis server,多執行緒環境下非執行緒安全,除非使用連線池,為每個 redis例項增加 物理連線。

Lettuce 是 一種可伸縮,執行緒安全,完全非阻塞的Redis客戶端,多個執行緒可以共享一個RedisConnection,它利用Netty NIO 框架來高效地管理多個連線,從而提供了非同步和同步資料訪問方式,用於構建非阻塞的反應性應用程式。

在 springboot 1.5.x版本的預設的Redis客戶端是 Jedis實現的,springboot 2.x版本中預設客戶端是用 lettuce實現的。

下面介紹 springboot 2.0分別使用 jedislettuce整合 redis服務

springboot 2.0 通過 lettuce整合Redis服務

匯入依賴

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

application.properties配置檔案

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=root
# 連線池最大連線數(使用負值表示沒有限制) 預設為8
spring.redis.lettuce.pool.max-active=8
# 連線池最大阻塞等待時間(使用負值表示沒有限制) 預設為-1
spring.redis.lettuce.pool.max-wait=-1ms
# 連線池中的最大空閒連線 預設為8
spring.redis.lettuce.pool.max-idle=8
# 連線池中的最小空閒連線 預設為 0
spring.redis.lettuce.pool.min-idle=0
複製程式碼

自定義 RedisTemplate

預設情況下的模板只能支援 RedisTemplate<String,String>,只能存入字串,很多時候,我們需要自定義 RedisTemplate ,設定序列化器,這樣我們可以很方便的操作例項物件。如下所示:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}
複製程式碼

定義測試實體類

public class User implements Serializable {
    private static final long serialVersionUID = 4220515347228129741L;
    private Integer id;
    private String username;
    private Integer age;

    public User(Integer id, String username, Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }

    public User() {
    }
    //getter/setter 省略
}
複製程式碼

測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {
    private Logger logger = LoggerFactory.getLogger(RedisTest.class);
    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @Test
    public void test() {
        String key = "user:1";
        redisTemplate.opsForValue().set(key, new User(1,"pjmike",20));
        User user = (User) redisTemplate.opsForValue().get(key);
        logger.info("uesr: "+user.toString());
    }
}
複製程式碼

springboot 2.0 通過 jedis 整合Redis服務

匯入依賴

因為 springboot2.0中預設是使用 Lettuce來整合Redis服務,spring-boot-starter-data-redis預設只引入了 Lettuce包,並沒有引入 jedis包支援。所以在我們需要手動引入 jedis的包,並排除掉 lettuce的包,pom.xml配置如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
複製程式碼

application.properties配置

使用jedis的連線池

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=root
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-active=8
複製程式碼

配置 JedisConnectionFactory

因為在 springoot 2.x版本中,預設採用的是 Lettuce實現的,所以無法初始化出 Jedis的連線物件 JedisConnectionFactory,所以我們需要手動配置並注入

public class RedisConfig {
    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        return factory;
    }
複製程式碼

但是啟動專案後發現報出瞭如下的異常:

jedis_error

redis連線失敗,springboot2.x通過以上方式整合Redis並不會讀取配置檔案中的 spring.redis.host等這樣的配置,需要手動配置,如下:

@Configuration
public class RedisConfig2 {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(JedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        return redisTemplate;
    }
    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPort(port);
        config.setPassword(RedisPassword.of(password));
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(config);
        return connectionFactory;
    }
}
複製程式碼

通過以上方式就可以連線上 redis了,不過這裡要提醒的一點就是,在springboot 2.x版本中 JedisConnectionFactory設定連線的方法已過時,如圖所示:

jedis_timeout

springboot 2.x版本中推薦使用 RedisStandaloneConfiguration類來設定連線的埠,地址等屬性

然後是單元測試,與上面 Lettuce的例子程式碼一樣,並且測試通過。

小結

上面介紹springboot 2.x版本如何通過 Jedis 和 Lettuce 來整合Redis服務,原始碼地址如下:github.com/pjmike/spri…

參考資料 & 鳴謝

相關文章