Redis Sentinel安裝與部署,實現redis的高可用

来源:https://www.cnblogs.com/youzhibing/archive/2018/02/24/8466491.html
-Advertisement-
Play Games

前言 對於生產環境,高可用是避免不了要面對的問題,無論什麼環境、服務,只要用於生產,就需要滿足高可用;此文針對的是redis的高可用。 接下來會有系列文章,該系列是對spring-session實現分散式集群session的共用的完整闡述,同時也引伸出緩存的實現;而此篇是該系列的第一篇。 githu ...


前言

  對於生產環境,高可用是避免不了要面對的問題,無論什麼環境、服務,只要用於生產,就需要滿足高可用;此文針對的是redis的高可用。

  接下來會有系列文章,該系列是對spring-session實現分散式集群session的共用的完整闡述,同時也引伸出緩存的實現;而此篇是該系列的第一篇。

  github地址:https://github.com/youzhibing/redis

環境準備

  redis版本:redis-3.0.0

  linux:centos6.7

  ip:192.168.11.202, 一臺伺服器上搭建搭建全部redis實例,包括數據節點實例以及哨兵(sentinel)實例

  客戶端jedis,基於spring-boot

redis主從複製

  搭建一主二從的主從環境

  1、redis安裝

    安裝很簡單,網上資料很多,redis官網也有說明;主要就是3步:解壓,make,make install

  2、數據節點配置文件

    redis解壓後,redis home目錄下有redis配置的樣例文件,我們不直接在此文件上就行修改,在redis home目錄下新建文件夾master_slave,將配置文件都放於此目錄下

    master配置文件:redis-6379.conf

port 6379
bind 192.168.11.202
requirepass "myredis"
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"
dir "/opt/soft/redis/data"

#如若master設置了認證密碼,那麼所有redis數據節點都配置上masterauth屬性
masterauth "myredis"

    slave-1配置文件:redis-6380.conf

port 6380
bind 192.168.11.202
requirepass "myredis"
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
dir "/opt/soft/redis/data"

#如若master設置了認證密碼,那麼所有redis數據節點都配置上masterauth屬性
masterauth "myredis"
slaveof 192.168.11.202 6379

    slave-2配置文件:redis-6381.conf

port 6381
bind 192.168.11.202
requirepass "myredis"
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
dir "/opt/soft/redis/data"

#如若master設置了認證密碼,那麼所有redis數據節點都配置上masterauth屬性
masterauth "myredis"
slaveof 192.168.11.202 6379

  3、節點啟動

    如下相關路徑需要根據自己的情況進行改動,可能和我的不一樣

[root@slave1 master_slave]# cd /opt/redis-3.0.0/master_slave/
[root@slave1 master_slave]# ./../src/redis-server redis-6379.conf 
[root@slave1 master_slave]# ./../src/redis-server redis-6380.conf 
[root@slave1 master_slave]# ./../src/redis-server redis-6381.conf

    確認主從關係

[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6379 -a myredis info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.11.202,port=6380,state=online,offset=393,lag=0
slave1:ip=192.168.11.202,port=6381,state=online,offset=393,lag=0
master_repl_offset:393
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:392

    還可以從從節點視角來看:[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6380 -a myredis info replication

  如若進行順利,按如上配置,一主二從環境搭建完畢

  4、從節點作用

    主要兩個作用:1、作為主節點的一個備份,一旦主節點出現故障,從節點可以作為後備"頂"上來,並且保證數據儘量不丟失(主從複製是最終一致性);2、從節點可以拓展主節點的能力,一旦主節點不能支撐大併發的讀操作,從節點可以在一定程度上幫助主節點分擔讀壓力

  5、主從複製問題

    1、一旦主節點出現故障,需要手動將一個從節點晉升為主節點,同時需要修改應用方的主節點地址,還需要命令其他從節點去複製新的主節點,整個過程需要人工干預

    2、主節點的寫能力受到單機的限制

    3、主節點的存儲能力受到單機的限制

redis sentinel部署

  此片主要講通過sentinel解決上述問題1,問題2、3則在下篇博客進行說明

  1、3個sentinel節點的配置文件基本一致,區別的只是埠

    sentinel-26379.conf

port 26379
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 192.168.11.202 6380 2
#redis數據master節點設置了認證,則需要如下配置
sentinel auth-pass mymaster myredis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

    sentinel-26380.conf

port 26380
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 192.168.11.202 6380 2
#redis數據master節點設置了認證,則需要如下配置
sentinel auth-pass mymaster myredis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

    sentinel-26381.conf

port 26381
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 192.168.11.202 6380 2
#redis數據master節點設置了認證,則需要如下配置
sentinel auth-pass mymaster myredis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

  2、啟動sentinel

[root@slave1 master_slave]# ./../src/redis-sentinel sentinel-26379.conf 
[root@slave1 master_slave]# ./../src/redis-sentinel sentinel-26380.conf 
[root@slave1 master_slave]# ./../src/redis-sentinel sentinel-26381.conf

  3、sentinel確認

[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=192.168.11.202:6379,slaves=2,sentinels=3

  Redis  Sentinel 最終拓撲結構

  4、高可用測試

    手動停掉6379實例或者kill掉6379實例

[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6379 -a myredis
192.168.11.202:6379> shutdown

    查看26379.log

    master節點已經自動切換到192.168.11.202:6380了,重啟6379,6379則是6378的從節點了。

[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6380 -a myredis
192.168.11.202:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.11.202,port=6381,state=online,offset=80802,lag=1
slave1:ip=192.168.11.202,port=6379,state=online,offset=80802,lag=1
master_repl_offset:80945
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:80944 

客戶端(Jedis)連接

  基於spring-boot開發,spring-boot-test測試, 這兩者本文不做說明,網上資料很多,不熟悉的自行去補充; 工程結構如下圖

  pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lee</groupId>
    <artifactId>redis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>redis</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>

    <dependencies>
        <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>
        
        <!-- 日誌過濾器 -->
        <dependency>
            <groupId>org.codehaus.janino</groupId>
            <artifactId>janino</artifactId>
        </dependency>
    </dependencies>
</project>
View Code

  redis-sentinel.properties

redis.masterName=mymaster
redis.sentinels=192.168.11.202:26379,192.168.11.202:26380,192.168.11.202:26381
redis.timeout=10000
#連接master需要用到的密碼,如果redis數據節點開啟了連接認證
redis.password=myredis

# 連接池
# 連接池最大連接數(使用負值表示沒有限制)
redis.pool.maxActive=150
# 連接池中的最大空閑連接
redis.pool.maxIdle=10
# 連接池中的最小空閑連接
redis.pool.minIdle=1
# 獲取連接時的最大等待毫秒數,小於零:阻塞不確定的時間,預設-1
redis.pool.maxWaitMillis=3000
# 每次釋放連接的最大數目
redis.pool.numTestsPerEvictionRun=50
# 釋放連接的掃描間隔(毫秒)
redis.pool.timeBetweenEvictionRunsMillis=3000
# 連接最小空閑時間(毫秒)
redis.pool.minEvictableIdleTimeMillis=1800000
# 連接空閑多久後釋放, 當空閑時間>該值 且 空閑連接>最大空閑連接數 時直接釋放(毫秒)
redis.pool.softMinEvictableIdleTimeMillis=10000
# 在獲取連接的時候檢查有效性, 預設false
redis.pool.testOnBorrow=true
# 在空閑時檢查有效性, 預設false
redis.pool.testWhileIdle=true
# 在歸還給pool時,是否提前進行validate操作
redis.pool.testOnReturn=true
# 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 預設true
redis.pool.blockWhenExhausted=true
View Code

  RedisConfig.java

package com.lee.redis.config;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

@Configuration
@PropertySource("redis/redis-sentinel.properties")
public class RedisConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisConfig.class);
    
    @Value("${redis.masterName}")
    private String masterName;
    @Value("${redis.sentinels}")
    private String sentinels;
    @Value("${redis.timeout}")
    private int timeout;
    @Value("${redis.password}")
    private String password;
    
    @Value("${redis.pool.maxActive}")
    private int maxTotal;

    @Value("${redis.pool.maxIdle}")
    private int maxIdle;

    @Value("${redis.pool.minIdle}")
    private int minIdle;

    @Value("${redis.pool.maxWaitMillis}")
    private long maxWaitMillis;

    @Value("${redis.pool.numTestsPerEvictionRun}")
    private int numTestsPerEvictionRun;

    @Value("${redis.pool.timeBetweenEvictionRunsMillis}")
    private long timeBetweenEvictionRunsMillis;

    @Value("${redis.pool.minEvictableIdleTimeMillis}")
    private long minEvictableIdleTimeMillis;

    @Value("${redis.pool.softMinEvictableIdleTimeMillis}")
    private long softMinEvictableIdleTimeMillis;

    @Value("${redis.pool.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${redis.pool.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${redis.pool.testOnReturn}")
    private boolean testOnReturn;

    @Value("${redis.pool.blockWhenExhausted}")
    private boolean blockWhenExhausted;
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Bean
    public JedisSentinelPool jedisSentinelPool(JedisPoolConfig poolConfig) {
        LOGGER.info("sentinel set : {}", sentinels);
        Set sentinelSet = new HashSet(Arrays.asList(sentinels.split(",")));
        JedisSentinelPool jedisPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout, password);
        return jedisPool;
    }
    
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
        jedisPoolConfig
                .setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        jedisPoolConfig
                .setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        jedisPoolConfig
                .setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        jedisPoolConfig.setTestWhileIdle(testWhileIdle);
        jedisPoolConfig.setTestOnReturn(testOnReturn);
        jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);

        return jedisPoolConfig;
    }
}
View Code

  Application.java

package com.lee.redis;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

    public static void main(String[] args) {
        
        SpringApplication app = new SpringApplication(Application.class);
        app.setBannerMode(Banner.Mode.OFF);            // 是否列印banner
        // app.setApplicationContextClass();        // 指定spring應用上下文啟動類
        app.setWebEnvironment(false);
        app.run(args);
    }
}
View Code

  RedisTest.java

package com.lee.redis;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisTest.class);
    
    @Autowired
    private JedisSentinelPool sentinelPool;
    
    @Test
    public void getNameTest() {
        Jedis jedis = null;
        try {
            jedis = sentinelPool.getResource();
            String name = jedis.get("name");
            System.out.println("name is " + name);
        } catch(Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        
    }
    
}
View Code

  更多詳情請上我的github

  運行RedisTest.java的getNameTest方法(name屬性已經在redis中設置,沒設置的需要提前設置),得到結果:

註意點

  1、有人可能會有這樣的疑問:為什麼通過sentinel來獲取redis的連接,而不是直接連接master來獲取redis連接呢?

    試想一下,客戶端直接通過master節點獲取redis連接,如果master節點掛掉了,雖然Redis Sentinel可以完成故障轉移,但是客戶端無法獲取這個變化,那麼客戶端就無法獲取redis連接了;

    最瞭解master節點信息的就是Sentinel節點集合,所以通過sentinel來獲取redis連接就能滿足高可用的要求了。

  2、redis master的故障轉移不影響客戶端連接代碼, 但是轉移期間內,通過sentinel是獲取不到主節點的連接的, 因為轉移期間內master節點還沒被選舉出來;

參考

  《Redis開發與運維》

  http://www.redis.cn/topics/sentinel.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、輸出重定向 命令 > 文件 將標準輸出重定向到一個文件中(清空原有文件的數據) 命令 2> 文件 將錯誤輸出重定向到一個文件中(清空原有文件的數據) 命令 >> 文件 將標準輸出重定向到一個文件中 (追加到原有內容的後面) 命令 2>> 文件 將錯誤輸出重定向到一個文件中(追加到原有內容的後面) ...
  • Tomcat介紹 Tomcat Tomcat運行環境 Tomcat安裝運行 Tomcat程式環境 ...
  • Varnish的子進程 VCL varnish的有限狀態機 客戶端和後端工作線程的詳細的varnish請求流程 VCL語法 VCL函數、關鍵字和變數 ...
  • varnish介紹 varnish varnish的特點 varnish的工作原理 varnish的優缺點 varnish設計原則 存儲對象 對象生命周期 varnish的應用程式 varnish安裝 varnish程式架構 varnish的程式環境 ...
  • 前言 本篇博文記錄結束之後,關於linux的基礎使用就要告上一段落了,下麵就真正開始了大數據相關內容的快速學習了。本篇博文要介紹的是linux的許可權管理以及相關的RPM軟體包的管理還有yum命令介紹。 一、Linux的許可權管理 1.1三種基本許可權 r——可讀許可權(read) w——可寫許可權(writ ...
  • ZooKeeper 是一個開源的分散式協調服務,由雅虎公司創建,是Google Chubby的開源實現,ZooKeeper的設計目標是將那些複雜且容易出錯的分散式一致性服務封裝起來,構成一個高效可靠的原語集。 ZooKeeper 是什麼 ZooKeeper是一個典型的分散式數據一致性的解決方案,分佈 ...
  • 本人遇到一次在安裝zabbix監控的時候,yum安裝的MySQL資料庫,後面用了一段時間發現data目錄下的ibdata1的空間特別大,反而我的zabbix資料庫的空間很小,這樣的情況在後面備份zabbix資料庫的時候會很不方便,所以想著要怎麼解決下。ibdata1文件是什麼? ibdata1是一個 ...
  • 1. 建表 2. 刪表 3. 常見數據類型 4. 聲明主碼 5. 聲明外碼 6. 聲明主碼,外碼的另一種方式 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...