Redis哨兵

博主以前寫了一篇Redis哨兵搭建,並無對哨兵進行講解,本篇填坑。html

同時,也爲博主寫Redis分佈式鎖(二)作一些前置知識。java

挖坑位置:Redis集羣搭建(哨兵)node

Redis主從

在講redis哨兵前,須要先簡單講解一下redis主從。web

俗話說,雞蛋放在一個籃子裏容易碎,那就把雞蛋複製一份,放到其餘籃子裏。全部的高可用基本都是這個思路。redis

上一篇文章講主從配置的時候,講到一個配置屬性slaveofspring

# 這個配置是redis-1中沒有的,須要在redis-2中新增
# 這裏的IP是redis-1的IP地址,端口是redis-1 6379.conf配置文件中port的值,默認值是6379
slaveof 1.1.1.1 6379

這個屬性就是配置redis主從的。apache

這裏分析如下上面這張圖,能夠發現如下幾個特色json

  • 客戶端能夠從三個redis中讀取數據
  • 只有主庫能夠寫入數據(抱歉,這個沒有在圖中體現)
  • 兩個從庫從主庫中讀取數據

這種redis架構解決了如下問題springboot

  • 主庫宕機,仍然能夠在從庫中讀取數據,必定程度上提升了可用性
  • 若是三個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數據
  • kill掉一個sentinel,再kill掉主庫,查看主庫切換

這裏博主再挖一坑,redis哨兵模式切換主庫的時候,如何通知運維人員呢?

總結

本文挖坑:

  • redis哨兵模式切換主庫的時候,如何通知運維人員呢?
相關文章
相關標籤/搜索