Redis哨兵(Sentinel)模式
主從切換技術的方法是:當主伺服器宕機後,需要手動把一臺從伺服器切換為主伺服器,這就需要人工干預,費事費力,還會造成一段時間內服務不可用。這不是一種推薦的方式,更多時候,我們優先考慮哨兵模式。
一、哨兵模式概述
哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個獨立的程序,作為程序,它會獨立執行。其原理是哨兵通過傳送命令,等待Redis伺服器響應,從而監控執行的多個Redis例項。
這裡的哨兵有兩個作用
通過傳送命令,讓Redis伺服器返回監控其執行狀態,包括主伺服器和從伺服器。
當哨兵監測到master宕機,會自動將slave切換成master,然後通過釋出訂閱模式
然而一個哨兵程序對Redis伺服器進行監控,可能會出現問題,為此,我們可以使用多個哨兵進行監控。各個哨兵之間還會進行監控,這樣就形成了多哨兵模式。
用文字描述一下故障切換(failover)的過程。假設主伺服器宕機,哨兵1先檢測到這個結果,系統並不會馬上進行failover過程,僅僅是哨兵1主觀的認為主伺服器不可用,這個現象成為主觀下線。當後面的哨兵也檢測到主伺服器不可用,並且數量達到一定值時,那麼哨兵之間就會進行一次投票,投票的結果由一個哨兵發起,進行failover操作。切換成功後,就會通過釋出訂閱模式,讓各個哨兵把自己監控的從伺服器實現切換主機,這個過程稱為客觀下線
二、Redis配置哨兵模式
配置3個哨兵和1主2從的Redis伺服器來演示這個過程。
服務型別 | 是否是主伺服器 | 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的主從伺服器,修改redis.conf檔案如下
# 使得Redis伺服器可以跨網路訪問
bind 0.0.0.0
# 設定密碼
requirepass "123456"
# 指定主伺服器,注意:有關slaveof的配置只是配置從伺服器,主伺服器不需要配置
slaveof 192.168.11.128 6379
# 主伺服器密碼,注意:有關slaveof的配置只是配置從伺服器,主伺服器不需要配置
masterauth 123456
上述內容主要是配置Redis伺服器,從伺服器比主伺服器多一個slaveof的配置和密碼。
配置3個哨兵,每個哨兵的配置都是一樣的。在Redis安裝目錄下有一個sentinel.conf檔案,copy一份進行修改
# 禁止保護模式
protected-mode no
# 配置監聽的主伺服器,這裡sentinel monitor代表監控,mymaster代表伺服器的名稱,可以自定義,192.168.11.128代表監控的主伺服器,6379代表埠,2代表只有兩個或兩個以上的哨兵認為主伺服器不可用的時候,才會進行failover操作。
sentinel monitor mymaster 192.168.11.128 6379 2
# sentinel author-pass定義服務的密碼,mymaster是服務名稱,123456是Redis伺服器密碼
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123456
上述關閉了保護模式,便於測試。
有了上述的修改,我們可以進入Redis的安裝目錄的src目錄,通過下面的命令啟動伺服器和哨兵
# 啟動Redis伺服器程序
./redis-server ../redis.conf
# 啟動哨兵程序
./redis-sentinel ../sentinel.conf
注意啟動的順序。首先是主機(192.168.11.128)的Redis服務程序,然後啟動從機的服務程序,最後啟動3個哨兵的服務程序。
三、Java中使用哨兵模式
/**
* 測試Redis哨兵模式
* @author liu
*/
public class TestSentinels {
@SuppressWarnings("resource")
@Test
public void testSentinel() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10);
jedisPoolConfig.setMaxIdle(5);
jedisPoolConfig.setMinIdle(5);
// 哨兵資訊
Set<String> sentinels = new HashSet<>(Arrays.asList("192.168.11.128:26379",
"192.168.11.129:26379","192.168.11.130:26379"));
// 建立連線池
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels,jedisPoolConfig,"123456");
// 獲取客戶端
Jedis jedis = pool.getResource();
// 執行兩個命令
jedis.set("mykey", "myvalue");
String value = jedis.get("mykey");
System.out.println(value);
}
}
上面是通過Jedis進行使用的,同樣也可以使用Spring進行配置RedisTemplate使用。
<bean id = "poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空閒數 -->
<property name="maxIdle" value="50"></property>
<!-- 最大連線數 -->
<property name="maxTotal" value="100"></property>
<!-- 最大等待時間 -->
<property name="maxWaitMillis" value="20000"></property>
</bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
<constructor-arg name="sentinelConfig" ref="sentinelConfig"></constructor-arg>
<property name="password" value="123456"></property>
</bean>
<!-- JDK序列化器 -->
<bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
<!-- String序列化器 -->
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="keySerializer" ref="stringRedisSerializer"></property>
<property name="defaultSerializer" ref="stringRedisSerializer"></property>
<property name="valueSerializer" ref="jdkSerializationRedisSerializer"></property>
</bean>
<!-- 哨兵配置 -->
<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"></property>
</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>
<constructor-arg name="port" value="26379"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="192.168.11.129"></constructor-arg>
<constructor-arg name="port" value="26379"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="192.168.11.130"></constructor-arg>
<constructor-arg name="port" value="26379"></constructor-arg>
</bean>
</set>
</property>
</bean>
四、哨兵模式的其他配置項
配置項 | 引數型別 | 作用 |
---|---|---|
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 down-after-milliseconds配置項只是一個哨兵在超過規定時間依舊沒有得到響應後,會自己認為主機不可用。對於其他哨兵而言,並不是這樣認為。哨兵會記錄這個訊息,當擁有認為主觀下線的哨兵達到sentinel monitor所配置的數量時,就會發起一次投票,進行failover,此時哨兵會重寫Redis的哨兵配置檔案,以適應新場景的需要。