一、環境準備redis
master : 192.168.50.10 6179 slave1: 192.168.50.10 6279 slave2: 192.168.50.10 6379
二、redis.conf配置文件配置緩存
master測試
port 6179 requirepass 123456 #密碼認證,能夠不設置 dir "/var/redis/6179" #工做目錄,dump.rdb會保留在這個目錄
slave1ui
port 6279 slaveof 192.168.50.10 6179 #master的ip和端口,不能使用127.0.0.1 6179 masterauth 123456 #若是master設置了requirepass,則這裏須要配置 slave-read-only yes #默認就是yes dir "/var/redis/6279"
slave23d
port 6379 slaveof 192.168.50.10 6179 #master的ip和端口,不能使用127.0.0.1 6179 masterauth 123456 #若是master設置了requirepass,則這裏須要配置 slave-read-only yes #默認就是yes dir "/var/redis/6379"
注意:若是slaveof 127.0.0.1 6179,則連接sentinel時提示是127.0.0.1,沒法鏈接日誌
三、啓動redis並測試 ==> 若是設置了daemonize yes則沒有日誌輸出,默認是nocode
啓動: nohup redis-server /redis/redis.conf $1>>/var/log/redis/redis.log 2>&1 &server
master 127.0.0.1:6179>info replicationblog
slave1 127.0.0.1:6279>info replication進程
slave2 127.0.0.1:6379>info replication
在master中執行以下語句,若是全部機器執行keys* 都能查出name這個key說明可以複製
127.0.0.1:6179> set name nametest
四、日誌分析
slave1和slave2一啓動就會去連接master
master啓動後,接受slave1和slave2的信息同步請求,進行RDB持久化(在內存中持久化)
五、redis主從同步規則
Salve會發送sync命令到Master Master啓動一個後臺進程,將Redis中的數據快照保存到文件中 啓動後臺進程的同時,Master會將保存數據快照期間接收到的寫命令緩存起來 Master完成寫文件操做後,將該文件發送給Salve Salve將文件保存到磁盤上,而後加載文件到內存恢復數據快照到Salve的Redis上 當Salve完成數據快照的恢復後,Master將這期間收集的寫命令發送給Salve端 後續Master收集到的寫命令都會經過以前創建的鏈接,增量發送給salve端
總結一下,主從剛剛鏈接的時候,進行全量同步;全同步結束後,進行增量同步。固然,若是有須要,slave 在任什麼時候候均可以發起全量同步
注意:在redis2.8開始,slave從新啓動,會發送psync到master進行部分同步,而不是sync全量同步
一、介紹
Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案,功能相似於zk,用於監控redis master狀態,進行切換。
sentinel是和redis獨立的組件,不須要每一個redis節點都啓動sentinel,能夠在任何機器上部署,只要可以鏈接redis master便可。
其功能相似於zk,用於監聽和選舉,能夠啓動多個sentinel做爲集羣,只要鏈接的master相同,則認爲是統一集羣,sentinel集羣中各個sentinel也有互相通訊,經過gossip協議
一個sentinel能夠監聽多個redis集羣,他們是經過鏈接到redis master,利用發佈/訂閱功能來發現其餘sentinel的存在。
二、配置參數介紹
集羣1 mymaster
# 至少2個sentinel認爲mymaster掛了,才真正認爲是掛了,因此sentinel至少要有2個在運行 sentinel monitor mymaster 192.168.50.10 6179 2 # sentinel向master發送ping,若是在這時間內沒有響應,則這個sentinel認爲master掛了 sentinel down-after-milliseconds mymaster 60000 # sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1
集羣2 resque
sentinel monitor resque 192.168.50.10 6179 4 sentinel down-after-milliseconds resque 10000 sentinel failover-timeout resque 180000 sentinel parallel-syncs resque 5
三、工做原理
連接master,獲取slave和其餘sentinel信息,記錄到本身的sentinel.conf中,以下圖
sentinel發現down-after-milliseconds 時間內master沒有回覆,則認爲failor,它會問集羣其餘sentinel是否也有人認爲失敗,個數達到sentinel monitor mymaster xxx 2,纔會觸發failover
sentinel集羣選舉其中一個sentinel拿着一個惟一的版本號去進行master切換。此sentinel指定一個slave發送SLAVE OF NO ONE,將其轉化爲master,同時將這些配置信息廣播給其餘sentinel,進行更新master信息
四、啓動 ===> 若是設置了daemonize yes則沒有日誌輸出
nohup redis-server redis-slave1/sentinel.conf --sentinel $1>>/var/log/sentinel/sentinel.log 2>&1 &
五、失敗測試=> 6379變成了6179的redis
六、注意
當一個master配置爲須要密碼才能鏈接時,客戶端和slave在鏈接時都須要提供密碼。 master經過requirepass設置自身的密碼,不提供密碼沒法鏈接到這個master。 slave經過masterauth來設置訪問master時的密碼。 可是當使用了sentinel時,因爲一個master可能會變成一個slave,一個slave也可能會變成master,因此須要同時設置上述兩個配置項。
一、pom依賴
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
二、代碼
public class JedisSentinelTest { public static void main(String[] args) { /** * 獲取master連接~方法一 */ HashSet<String> sentinelSet = new HashSet<>(); sentinelSet.add("192.168.50.10:26179"); // 這個節點目前是掛掉的,也能夠配置,後續啓動有效 sentinelSet.add("192.168.50.10:26279"); sentinelSet.add("192.168.50.10:26379"); JedisSentinelPool myMasterSentinel = new JedisSentinelPool("mymaster", sentinelSet); Jedis master = myMasterSentinel.getResource(); master.set("aaaa", "aaaTest"); master.close(); /** * 獲取master連接~方法二 */ Jedis sentinelNode = new Jedis("192.168.50.10", 26179); List<Map<String, String>> masterList = sentinelNode.sentinelMasters(); Map<String, String> masterMap = null; for (Map<String, String> temp : masterList){ if ("mymaster".equals(temp.get("name"))){ masterMap = temp; break; } } System.out.println("masterIp:" + masterMap.get("ip")); System.out.println("masterPort:" + masterMap.get("port")); List<String> mymaster = sentinelNode.sentinelGetMasterAddrByName("mymaster"); System.out.println("masterIp:" + mymaster.get(0)); System.out.println("masterPort:" + mymaster.get(1)); /** * 獲取slaves */ Jedis jedis = new Jedis("192.168.50.10", 26179); List<Map<String, String>> slaves = jedis.sentinelSlaves("mymaster"); for (Map<String, String> temp : slaves){ System.out.println("slaveIp:" + temp.get("ip")); System.out.println("slaveIp:" + temp.get("port")); } /** * 主從切換 */ Map<String, String> first = slaves.get(0); Jedis toBeMaster = new Jedis(first.get("ip"), Integer.parseInt(first.get("port"))); String flag = toBeMaster.slaveofNoOne(); // 將本身設置爲主節點 for (int i = 1; i < slaves.size(); i++){ Map<String, String> temp = slaves.get(i); Jedis ortherSlave = new Jedis(temp.get("ip"), Integer.parseInt(temp.get("port"))); ortherSlave.slaveof(first.get("ip"), Integer.parseInt(first.get("port"))); } Jedis slave = new Jedis("192.168.50.10", 6279); /** * 從新加載配置信息,復原 */ slave.configResetStat(); /** * 手動觸發RDB(save、bgsave)以及aof rewrite */ slave.save(); slave.bgsave(); slave.bgrewriteaof(); /** * 獲取信息,能夠知道本身是什麼角色 */ String info = slave.info(); String replication = slave.info("replication"); } }