Redis哨兵
部落客之前寫了一篇Redis哨兵搭建,並沒有對哨兵進行講解,本篇填坑。
同時,也為部落客寫Redis分散式鎖(二)做一些前置知識。
挖坑位置:Redis集群搭建(哨兵)
Redis主從
在講redis哨兵前,需要先簡單講解一下redis主從。
俗話說,雞蛋放在一個籃子里容易碎,那就把雞蛋複製一份,放到其他籃子里。所有的高可用基本都是這個思路。
上一篇文章講主從配置的時候,講到一個配置屬性slaveof
# 這個配置是redis-1中沒有的,需要在redis-2中新增
# 這裡的IP是redis-1的IP地址,埠是redis-1 6379.conf配置文件中port的值,默認值是6379
slaveof 1.1.1.1 6379
這個屬性就是配置redis主從的。
這裡分析以下上面這張圖,可以發現以下幾個特點
- 客戶端可以從三個redis中讀取數據
- 只有主庫可以寫入數據(抱歉,這個沒有在圖中體現)
- 兩個從庫從主庫中讀取數據
這種redis架構解決了以下問題
- 主庫宕機,仍然可以在從庫中讀取數據,一定程度上提高了可用性
- 如果三個redis運行正常,數據應該一致
- 讀取的壓力分擔到了3個節點上
存在以下幾個問題
- 主庫宕機,不能寫入
- 主庫不能自動切換,需要手動切換
- ※主庫寫入數據成功,還沒來得及同步到從庫,主庫宕機
基於以上的一些問題,我們引出了redis哨兵
Redis哨兵
主從中存在一些問題是我們不能接受的,比如,主庫宕機=無法寫入。我們當然期望,宕機一個節點的時候,仍然可以對外服務,這才是高可用嘛~
哨兵
假如不能自動切換主庫,我們該怎麼做呢?運維童鞋,先不管主庫了(已經宕機了),在從庫中選擇一個作為新的主庫啟動,優先提供服務嘛~至於主庫,稍後再分析宕機原因,解決問題。
本著軟體能解決的問題,就不使用人力,我們可不可以下一個軟體,來代替運維童鞋的這些操作?
這個就是哨兵的功能雛形了。
哨兵監視主從redis,一旦redis主庫宕機,哨兵切換主庫,客戶端再寫入數據的時候,向新的主庫中寫入。在切換的時候,給運維童鞋一條通知,運維童鞋再處理。是不是很6?
優化
從上面可以看出,我們只有一個哨兵,如果這個哨兵宕機了,那我們的保障就沒有了。
我們可以把哨兵也集群起來,優化結構如下:
分析
你可以把哨兵理解為zookeeper或者是eureka,哨兵相當於一個註冊中心(當然不僅僅是註冊中心的功能),客戶端也從哨兵讀取redis主從庫資訊。三個哨兵組成了一個集群的註冊中心,當有一個哨兵宕機,還有其他哨兵存活,依然可以服務。
這樣哨兵Sentinel也集群起來了,redis也有主從,我們這個時候可以說,提供了一套高可用的redis。
但這樣的架構並不是絕對完美的,仍然存在一些問題。
依舊不能解決主從中的一個問題「主庫寫入數據成功,還沒來得及同步到從庫,主庫宕機」。
例如主庫上寫入了一把鎖,還沒來得及把鎖的資訊同步到從庫,主庫掛了,從庫切換為了主庫,然而這個新的主庫上並沒有鎖。引起了鎖失效。
如何解決呢?
-
手動調整數據吧,少年。
-
如果是鎖的場景,可以用zookeeper來代替redis分散式鎖來解決。
哨兵客戶端實現
部落客使用的springboot來演示,使用工具包lettuce
依賴
<!-- data-redis中集成了lettuce -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis鏈接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- alibaba json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
配置文件
server:
port: 80
spring:
redis:
password: 密碼
sentinel:
# 這個配置在哨兵配置中
master: 你的集群名稱
# 26379 埠是哨兵的默認埠
nodes: 10.101.36.19:26379,10.101.36.20:26379,10.101.36.21:26379
lettuce:
pool:
# 最大鏈接數
max-active: 30
# 鏈接池中最大空閑鏈接數
max-idle: 15
# 最大阻塞等待鏈接時長 默認不限制 -1
max-wait: 2000
# 最小空閑鏈接數
min-idle: 10
# 鏈接超時時長
shutdown-timeout: 10000
Redis 配置類
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
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.StringRedisSerializer;
/**
* redis 配置類 將RedisTemplate交給spring託管
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
測試類
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("test")
public class TestController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping
public String Test(){
redisTemplate.opsForValue().set("xujp", "sentinel");
String rst = (String) redisTemplate.opsForValue().get("xujp");
return rst;
}
}
測試
postman直接get請求即可
在redis連接工具中查看
主庫:
從庫:
這個時候把主庫手動關閉
查看哨兵日誌
可以看到主節點已切換完成
26721:X 20 Jul 2020 15:08:40.941 # +switch-master mymaster 10.101.36.20 6379 10.101.36.19 6379
這時候,在postman中再次發送請求
請求成功。
這裡部落客再挖一坑,redis哨兵模式切換主庫的時候,如何通知運維人員呢?
總結
本文挖坑:
- redis哨兵模式切換主庫的時候,如何通知運維人員呢?