今天小七給大夥介紹一下,如何在Spring Boot項目中使用redis,大家都知道搭建redis有4種方式,分别是單節點執行個體、主從模式、sentinel模式、cluster模式。今天來給大夥介紹一下sentinel模式的環境搭建以及如何內建到Spring Boot中。
先來簡單介紹一下sentinel模式,也就是哨兵模式。哨兵模式是一種特殊的模式,首先Redis提供了哨兵的指令,哨兵是一個獨立的程序,作為程序,它會獨立運作。其原理是哨兵通過發送指令,等待Redis伺服器響應,進而監控運作的多個Redis執行個體。
哨兵有兩個作用:
- 通過發送指令,讓Redis伺服器傳回監控其運作狀态,包括主伺服器和從伺服器;
- 當哨兵監測到master當機,會自動将slave切換成master,然後通過釋出訂閱模式通知其他的從伺服器,修改配置檔案,讓它們切換主機。
下面,小七分以下幾個步驟給大家介紹一下:
(一)Windows下哨兵模式的安裝
因為redis官網沒有提供windows安裝,是以咱們從下面的位址下載下傳即可,https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100。
下載下傳Redis-x64-3.2.100.zip後,進行解壓,檔案夾内容如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSPBpWTxMWbihGdyoVd5cVZwZ0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwkzMzITM0AjM3ADNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
标紅的10個檔案是小七新加的,下面小七來分别介紹一下:
主節點:redis.conf、redis-startup.bat
從節點1:redis2.conf、redis2-startup.bat
從節點2:redis3.conf、redis3-startup.bat
哨兵1:sentinel.conf、sentinel-startup.bat
哨兵2:sentinel2.conf、sentinel2-startup.bat
說明一下,.conf是配置檔案,.bat是友善啟動弄的一個批處理檔案。
redis.conf、redis2.conf、redis3.conf直接拷貝redis.windows.conf即可,需要改下裡面的端口,然後從節點需要指定主節點。
redis.conf(主):
port 6379
redis2.conf(從1):
port 6380
slaveof 127.0.0.1 6379
redis3.conf(從2):
port 6381
slaveof 127.0.0.1 6379
然後,再來看下相應的批處理檔案,如下:
redis-startup.bat:
redis-server.exe redis.conf
redis2-startup.bat:
redis-server.exe redis2.conf
redis3-startup.bat:
redis-server.exe redis3.conf
下面,再來看看哨兵的配置檔案。
sentinel.conf:
#目前Sentinel服務運作的端口
port 26379
#關閉保護模式
protected-mode no
# 哨兵監聽的主伺服器
sentinel monitor myMaster 127.0.0.1 6379 2
# 3s内myMaster無響應,則認為myMaster當機了
sentinel down-after-milliseconds myMaster 3000
# 執行故障轉移時,最多有1個從伺服器同時對新的主伺服器進行同步
sentinel parallel-syncs myMaster 1
#如果10秒後,myMaster仍沒啟動過來,則啟動failover
sentinel failover-timeout myMaster 10000
sentinel2.conf:
#目前Sentinel服務運作的端口
port 36379
#關閉保護模式
protected-mode no
# 哨兵監聽的主伺服器
sentinel monitor myMaster 127.0.0.1 6379 2
# 3s内myMaster無響應,則認為myMaster當機了
sentinel down-after-milliseconds myMaster 3000
# 執行故障轉移時,最多有1個從伺服器同時對新的主伺服器進行同步
sentinel parallel-syncs myMaster 1
#如果10秒後,myMaster仍沒啟動過來,則啟動failover
sentinel failover-timeout myMaster 10000
兩個哨兵的配置除了端口不一樣,其它都一樣即可。
然後,相應的批處理檔案如下。
sentinel-startup.bat:
redis-server.exe sentinel.conf --sentinel
sentinel2-startup.bat:
redis-server.exe sentinel2.conf --sentinel
好啦,接下來按以下順序執行,直接輕按兩下即可:
redis-startup.bat
redis2-startup.bat
redis3-startup.bat
sentinel-startup.bat
sentinel2-startup.bat
下面,小七截個圖看看啟動日志:
主節點:
從1:
哨1:
(二)如何測試哨兵模式是否正常啟動
先來檢查一些Master是否正常,用redis用戶端連上,指令如下:
redis-cli.exe -h 127.0.0.1 -p 6379
然後,執行指令:
info replication
結果如下:
說明主從節點啟動都是正常的。
下面再來看看哨兵是否正常,執行以下指令連上sentinel:
redis-cli.exe -h 127.0.0.1 -p 26379
接着執行:
info sentinel
結果如下:
OK,一切正常,說明哨兵模式啟動沒有問題。
然後,我們把主節點給關掉,再來看看哨兵的日志:
可以看到,自動選舉了一個從節點為主節點了,說明哨兵的作用生效了。
(三)pom.xml依賴配置
這個小七直接貼出來,如下:
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Jedis 用戶端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
這裡注意,Spring Boot2.X預設選擇使用lettuce用戶端,下面簡單介紹一下:
Lettuce是一個高性能基于Java編寫的Redis驅動架構,底層內建了Project Reactor提供天然的反應式程式設計,通信架構內建了Netty使用了非阻塞IO,5.x版本之後融合了JDK1.8的異步程式設計特性,在保證高性能的同時提供了十分豐富易用的API。
commons-pool2是用于連接配接池。
(四)代碼測試是否能連接配接哨兵模式
下面,咱們再來寫個測試代碼,看能否連接配接到哨兵模式,代碼如下:
@Test
public void testConnectSentinel() {
Set<String> sentinels = new HashSet<>();
sentinels.add("127.0.0.1:26379");
sentinels.add("127.0.0.1:36379");
String clusterName = "myMaster";
JedisSentinelPool redisSentinelPool = new JedisSentinelPool(clusterName, sentinels);
Jedis jedis = null;
try {
jedis = redisSentinelPool.getResource();
jedis.set("name01", "aaa");
System.out.println(jedis.get("name01"));
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
redisSentinelPool.close();
}
}
運作結果如下:
咱們也可以下載下傳一個Redis GUI來看一下,如下圖:
說明連接配接哨兵模式沒有問題。
(五)Spring Boot中如何配置哨兵模式
首先,咱們看一下.yml配置,如下圖:
spring:
redis:
# host: 127.0.0.1
# port: 6379
password:
database: 0 # Redis資料庫索引(預設為0)
timeout: 2000ms # 連接配接逾時時間(毫秒)預設是2000ms
lettuce:
pool:
max-active: 200 # 連接配接池最大連接配接數(使用負值表示沒有限制)
max-wait: -1ms # 連接配接池最大阻塞等待時間(使用負值表示沒有限制)
max-idle: 100 # 連接配接池中的最大空閑連接配接
min-idle: 50 # 連接配接池中的最小空閑連接配接
shutdown-timeout: 100ms
sentinel: # 哨兵模式
master: myMaster
nodes: 127.0.0.1:26379,127.0.0.1:36379
這裡如果不想使用哨兵模式,把sentinel注釋,放開host和port就可以啦。
(六)自定義redis的key和value的序列化和反序列化
因為redis是以鍵值對的方式儲存資料,儲存的時候會進行序列化,如果不指定的話,就會按對象預設的序列化方式進行,這時咱們去redis檢視的時候,會完全看不懂是啥。一般,這咱們可以JSON格式進行序列化和反序列化,容易進行管理。
首先,咱們建立一個配置類RedisConfig,代碼如下:
package org.qyk.springboot.redis;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* <Change to the actual description of this class>
*
* @version 1.0
* <pre>
* Author Date Changes
* yongkang.qi 2020年04月06日 Created
*
* </pre>
* @since 1.7
*/
@Configuration
@EnableCaching //開啟注解
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置連接配接工廠
template.setConnectionFactory(redisConnectionFactory);
//使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值(預設使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSerial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修飾符範圍,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化輸入的類型,類必須是非final修飾的,final修飾的類,比如String,Integer等會跑出異常
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jacksonSerial.setObjectMapper(om);
// 值采用json序列化
template.setValueSerializer(jacksonSerial);
//使用StringRedisSerializer來序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 設定hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jacksonSerial);
template.afterPropertiesSet();
return template;
}
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
};
}
}
這裡,主要是看redisTemplate這個方法即可。
咱們寫個單元測試類,如下:
package org.qyk.springboot.redis;
import java.util.*;
import com.alibaba.fastjson.JSON;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.qyk.springboot.dao3.PersonDao;
import org.qyk.springboot.entity.PersonEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import javax.annotation.Resource;
/**
* <Change to the actual description of this class>
*
* @version 1.0
* <pre>
* Author Date Changes
* yongkang.qi 2020年04月06日 Created
*
* </pre>
* @since 1.7
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Resource
private PersonDao personDao;
@Test
public void testConnectSentinel() {
Set<String> sentinels = new HashSet<>();
sentinels.add("127.0.0.1:26379");
sentinels.add("127.0.0.1:36379");
String clusterName = "myMaster";
JedisSentinelPool redisSentinelPool = new JedisSentinelPool(clusterName, sentinels);
Jedis jedis = null;
try {
jedis = redisSentinelPool.getResource();
jedis.set("name01", "aaa");
System.out.println(jedis.get("name01"));
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
redisSentinelPool.close();
}
}
@Test
public void testStringRedisTemplate() {
stringRedisTemplate.opsForValue().set("name02", "小七2");
Assert.assertEquals("小七2", stringRedisTemplate.opsForValue().get("name02"));
}
@Test
public void testRedisTemplate() {
PersonEntity entity = new PersonEntity();
entity.setUsername("小七");
entity.setAge(18);
entity.setBirth(new Date());
redisTemplate.opsForValue().set("name03", entity);
// PersonEntity entity = (PersonEntity) redisTemplate.opsForValue().get("name03");
// System.out.println(JSON.toJSONString(entity));
}
@Test
public void testCacheable() {
List<Map<String, Object>> maps = personDao.queryList("%%");
System.out.println(JSON.toJSONString(maps));
}
}
運作一下testRedisTemplate方法,然後我們來看一下圖形化界面,結果如下:
這裡可以看到,key和value都是按咱們自定義的序列化方式進行儲存的。
(七)如何給方法添加緩存
有的時候,咱們可能需要給某個查詢方法增加緩存,無須一直查詢資料庫,提升查詢速度。那麼這個時候該怎麼辦呢,Spring Boot提供了注解的方式,使用起來非常友善。
首先咱們先配置一下keyGenerator,就是RedisConfig類的keyGenerator,待會兒示範一下就知道是幹嘛用的了。
然後咱們要開啟緩存注解,也就是@EnableCaching,在RedisConfig加上既可以。
下面,咱們來給一個方法加上緩存,如下:
即加上@Cacheable(value = “sampleCache”)就行了。
下面咱們運作一下單元測試類中的testCacheable方法,然後檢查一下緩存,如下:
看到上面的key,大家就應該能明白keyGenerator的作用了吧。
然後,咱們再來運作一次,就會發現queryList的代碼沒有被再執行,直接取了緩存中的值。這個小七就不示範了。
(八)結語
好了,今天就可以介紹到這裡了。其實,涉及到redis的技術點還是相當多的,小七主要是給大夥介紹在Spring Boot項目中內建redis,具體的詳細使用,大家可以去自己研究研究。總之,Spring Boot大大提升了咱們內建使用一些技術架構的效率,減少了很多繁瑣的配置,用起來确實很舒服,不過,就是得多查查改如何使用~