Redis哨兵(Sentinel)模式的配置方法及其在Java中的用法
主從切換技術的方法是:當主服務器宕機后,需要手動把一台從服務器切換為主從服務器,這就需要人工干預,既費時費力,還會造成一段時間內服務不可用,這不是一種推薦的方式,因此筆者沒有介紹主從切換技術。
更多的時候,我們優先考慮哨兵模式,它是當前企業應用的主流方式。
其原理是哨兵通過發送命令,等待 Redis 服務器響應,從而監控運行的多個 Redis 實例,如圖 1 所示。
圖 1 Redis哨兵
這里的哨兵有兩個作用:
只是現實中一個哨兵進程對 Redis 服務器進行監控,也可能出現問題,為了處理這個問題,還可以使用多個哨兵的監控,而各個哨兵之間還會相互監控,這樣就變為了多個哨兵模式。多個哨兵不僅監控各個 Redis 服務器,而且哨兵之間互相監控,看看哨兵們是否還“活”着,其關系如圖 2 所示。
論述一下故障切換(failover)的過程。假設主服務器宕機,哨兵 1 先監測到這個結果,當時系統並不會馬上進行 failover 操作,而僅僅是哨兵 1 主觀地認為主機已經不可用,這個現象被稱為主觀下線。
當后面的哨兵監測也監測到了主服務器不可用,並且有了一定數量的哨兵認為主服務器不可用,那么哨兵之間就會形成一次投票,投票的結果由一個哨兵發起,進行 failover 操作,在 failover 操作的過程中切換成功后,就會通過發布訂閱方式,讓各個哨兵把自己監控的服務器實現切換主機,這個過程被稱為客觀下線。這樣對於客戶端而言,一切都是透明的。
圖 2 多哨兵監控Redis
配置 3 個哨兵和 1 主 2 從的 Redis 服務器來演示這個過程。機器的分配,如表 1 所示:
它的結構就如同圖 2 所示,下面對它們進行配置。首先配置 Redis 的主從服務器,修改服務器的 redis.conf 文件,下面的配置內容,僅僅是筆者在原有文件的基礎上修改的內容:
配置 3 個哨兵,每一個哨兵的配置都是一樣的,在 Redis 安裝目錄下可以找到 sentinel.conf 文件,然后對其進行修改。
下面對 3 個哨兵的這個文件作出修改,同樣也是在原有的基礎上進行修改,如下所示。
有了上述的修改,我們可以進入 Redis 的安裝目錄下的 src 目錄,通過以下命令啟動 Redis 服務器和哨兵,如下所示:
從圖 3 中,我們看到了一個哨兵對多台 Redis 服務器進行了監控。
圖 3 哨兵監控信息
通過上述的代碼就能夠連接 Redis 服務器了,這個時候將啟動主機(192.168.11.128)提供服務。為了驗證哨兵的作用,我們可以把主機上的 Redis 服務器關閉,馬上運行,你就可以發現報錯,那倒不是因為哨兵失效導致的,而是因為 Redis 哨兵默認超時 3 分鍾后才會進行投票切換主機,等超過 3 分鍾后再進行測試。
同樣的,通過配置也可以實現在 Spring 中使用哨兵的這些功能,代碼如下所示。
這樣就在 Spring 中配置好了哨兵和其他的內容,使用 Spring 測試哨兵,代碼如下所示。
這樣在實際的項目中,就可以使用哨兵模式來提高系統的可用性和穩定了。
sentinel down-after-milliseconds 配置項只是一個哨兵在超過其指定的毫秒數依舊沒有得到回答消息后,會自己認為主機不可用。對於其他哨兵而言,並不會認為主機不可用。
哨兵會記錄這個消息,當擁有認為主觀下線的哨兵到達 sentinel monitor 所配置的數量的時候,就會發起一次新的投票,然后切換主機,此時哨兵會重寫 Redis 的哨兵配置文件,以適應新場景的需要。
更多的時候,我們優先考慮哨兵模式,它是當前企業應用的主流方式。
哨兵模式概述
Redis 可以存在多台服務器,並且實現了主從復制的功能。哨兵模式是一種特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一個獨立的進程,作為進程,它會獨立運行。其原理是哨兵通過發送命令,等待 Redis 服務器響應,從而監控運行的多個 Redis 實例,如圖 1 所示。

圖 1 Redis哨兵
這里的哨兵有兩個作用:
- 通過發送命令,讓 Redis 服務器返回監測其運行狀態,包括主服務器和從服務器。
- 當哨兵監測到 master 宕機,會自動將 slave 切換成 master,然后通過發布訂閱模式通知到其他的從服務器,修改配置文件,讓它們切換主機。
只是現實中一個哨兵進程對 Redis 服務器進行監控,也可能出現問題,為了處理這個問題,還可以使用多個哨兵的監控,而各個哨兵之間還會相互監控,這樣就變為了多個哨兵模式。多個哨兵不僅監控各個 Redis 服務器,而且哨兵之間互相監控,看看哨兵們是否還“活”着,其關系如圖 2 所示。
論述一下故障切換(failover)的過程。假設主服務器宕機,哨兵 1 先監測到這個結果,當時系統並不會馬上進行 failover 操作,而僅僅是哨兵 1 主觀地認為主機已經不可用,這個現象被稱為主觀下線。
當后面的哨兵監測也監測到了主服務器不可用,並且有了一定數量的哨兵認為主服務器不可用,那么哨兵之間就會形成一次投票,投票的結果由一個哨兵發起,進行 failover 操作,在 failover 操作的過程中切換成功后,就會通過發布訂閱方式,讓各個哨兵把自己監控的服務器實現切換主機,這個過程被稱為客觀下線。這樣對於客戶端而言,一切都是透明的。

圖 2 多哨兵監控Redis
搭建哨兵模式
配置 3 個哨兵和 1 主 2 從的 Redis 服務器來演示這個過程。機器的分配,如表 1 所示:
服務類型 | 是否主服務器 | IP地址 | 端口 |
---|---|---|---|
Redis | 是 | 192.168.11.128 | 6379 |
Redis | 否 | 192.168.11.129 | 6379 |
Redis | 否 | 192.168.11.130 | 6379 |
Sentinel | —— | 192.168.11.128 | 26379 |
Sentinel | —— | 192.168.11.129 | 26379 |
Sentinel | —— | 192.168.11.130 | 26379 |
#使得Redis服務器可以跨網絡訪問
bind 0.0.0.0
#設置密碼
requiredpass "abcdefg"
#指定主服務器,注意:有關slaveof的配置只是配置從服務器,而主服務器不需要配置
slaveof 192.168.11.128 6379
#主服務器密碼,注意:有關slaveof的配置只是配置從服務器,而主服務器不需要配置
masterauth abcdefg
配置 3 個哨兵,每一個哨兵的配置都是一樣的,在 Redis 安裝目錄下可以找到 sentinel.conf 文件,然后對其進行修改。
下面對 3 個哨兵的這個文件作出修改,同樣也是在原有的基礎上進行修改,如下所示。
#禁止保護模式
protected-mode no
#配置監聽的主服務器,這里 sentinel monitor 代表監控
#mymaster代表服務器名稱,可以自定義
#192.168.11.128代表監控的主服務器
#6379代表端口
#2代表只有兩個或者兩個以上的燒餅認為主服務器不可用的時候,才會做故障切換操作
sentinel monitor mymaster 192.168.11.128 6379 2
#sentinel auth-pass 定義服務的密碼
#mymaster服務名稱
#abcdefg Redis服務器密碼
sentinel auth-pass mymaster abcdefg
有了上述的修改,我們可以進入 Redis 的安裝目錄下的 src 目錄,通過以下命令啟動 Redis 服務器和哨兵,如下所示:
#啟動哨兵進程
./redis-sentinel ../sentinel.conf
#啟動Redis服務器進程
./redis-server ../redis.conf
從圖 3 中,我們看到了一個哨兵對多台 Redis 服務器進行了監控。
在 Java 中使用哨兵模式
在 Java 中使用哨兵模式,加入關於哨兵的信息即可,非常簡單,下面代碼展示了這樣的一個過程。- //連接池配置
- JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
- jedisPoolConfig.setMaxTotal(10);
- jedisPoolConfig.setMaxIdle(5);
- jedisPoolConfig.setMinIdle(5);
- //哨兵信息
- Set<String> sentinels = new HashSet<String>(Arrays.asList(
- "192.168.11.128:26379",
- "192.168.11.129:26379",
- "192.168.11.130:26379"
- ));
- //創建連接池
- //mymaster是我們配置給哨兵的服務名稱
- //sentinels是哨兵信息
- //jedisPoolConfig是連接池配置
- //abcdefg是連接Redis服務器的密碼
- JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels, jedisPoolConfig, "abcdefg");
- //獲取客戶端
- Jedis jedis = pool.getResource();
- //執行兩個命令
- jedis.set("mykey", "myvalue");
- String myvalue = jedis.get("mykey");
- //打印信息
- System.out.println(myvalue);

圖 3 哨兵監控信息
通過上述的代碼就能夠連接 Redis 服務器了,這個時候將啟動主機(192.168.11.128)提供服務。為了驗證哨兵的作用,我們可以把主機上的 Redis 服務器關閉,馬上運行,你就可以發現報錯,那倒不是因為哨兵失效導致的,而是因為 Redis 哨兵默認超時 3 分鍾后才會進行投票切換主機,等超過 3 分鍾后再進行測試。
同樣的,通過配置也可以實現在 Spring 中使用哨兵的這些功能,代碼如下所示。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
- <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
- <!-- 最大空閑數 -->
- <property name="maxIdle" value="50" />
- <!-- 最大連接數 -->
- <property name="maxTotal" value="100" />
- <!-- 最大等待時間 -->
- <property name="maxWaitMillis" value="30000" />
- </bean>
- <!-- jdk序列化器,可保存對象 -->
- <bean id="jdkSerializationRedisSerializer"
- class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
- <!-- String序列化器 -->
- <bean id="stringRedisSerializer"
- class="org.springframework.data.redis.serializer.StringRedisSerializer" />
- <!-- 哨兵配置 -->
- <bean id="sentinelConfig"
- class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
- <!-- 服務名稱 -->
- <property name="master">
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <property name="name" value="mymaster" />
- </bean>
- </property>
- <!-- 哨兵服務IP和端口 -->
- <property name="sentinels">
- <set>
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <constructor-arg name="host" value="192.168.11.128" />
- <constructor-arg name="port" value="26379" />
- </bean>
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <constructor-arg name="host" value="192.168.11.129" />
- <constructor-arg name="port" value="26379" />
- </bean>
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <constructor-arg name="host" value="192.168.11.130" />
- <constructor-arg name="port" value="26379" />
- </bean>
- </set>
- </property>
- </bean>
- <!-- 連接池設置 -->
- <bean id="connectionFectory"
- class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
- <constructor-arg name="sentinelConfig" ref="sentinelConfig" />
- <constructor-arg name="poolConfig" ref="poolConfig" />
- <property name="password" value="abcdefg" />
- </bean>
- <!-- 配置 RedisTemplate -->
- <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
- <property name="connectionFactory" ref="connectionFactory" />
- <property name="keySerializer" ref="stringRedisSerializer" />
- <property name="defaultSerializer" ref="stringRedisSerializer" />
- <property name="valueSerializer" ref="jdkSerializationRedisSerializer" />
- </bean>
- </beans>
- public static void testLuaFile() {
- ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
- RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
- String retVal = (String)redisTemplate.execute((RedisOperations ops)->{
- ops.boundValueOps("mykey").set("myvalue");
- String value = (String)ops.boundValuesOps("mykey").get();
- return value;
- });
- System.out.println(retVal);
- }
哨兵模式的其他配置項
上述以最簡單的配置完成了哨兵模式。哨兵模式的配置項還是比較有用的,比如上述我們需要等待 3 分鍾后,Redis 哨兵進程才會做故障切換,有時候我們希望這個時間短一些,下面再對它們進行一些介紹,如表 2 所示。配置項 | 參數類型 | 作用 |
---|---|---|
port | 整數 | 啟動哨兵進程端口 |
dir | 文件夾目錄 | 哨兵進程服務臨時文件夾,默認為 /tmp,要保證有可寫入的權限 |
sentinel down-after-milliseconds | <服務名稱><亳秒數(整數)> | 指定哨兵在監測 Redis 服務時,當 Redis 服務在一個亳秒數內都無 法回答時,單個哨兵認為的主觀下線時間,默認為 30000(30秒) |
sentinel parallel-syncs | <服務名稱><服務器數(整數)> | 指定可以有多少 Redis 服務同步新的主機,一般而言,這個數字越 小同步時間就越長,而越大,則對網絡資源要求則越高 |
sentinel failover-timeout | <服務名稱><亳秒數(整數)> | 指定在故障切換允許的亳秒數,當超過這個亳秒數的時候,就認為 切換故障失敗,默認為 3 分鍾 |
sentinel notification-script | <服務名稱><腳本路徑> | 指定 sentinel 檢測到該監控的 redis 實例指向的實例異常時,調用的 報警腳本。該配置項可選,比較常用 |
哨兵會記錄這個消息,當擁有認為主觀下線的哨兵到達 sentinel monitor 所配置的數量的時候,就會發起一次新的投票,然后切換主機,此時哨兵會重寫 Redis 的哨兵配置文件,以適應新場景的需要。