Java實(shí)現(xiàn)Redis哨兵的示例代碼
前言:
本文將采用文字+代碼的方式,講解redis版哨兵的實(shí)現(xiàn),所有代碼都將寫在一個(gè)類中,每個(gè)屬性和方法都會(huì)結(jié)合文字加以說明。
1. 哨兵(Sentinel)主要功能如下:
1、不時(shí)的監(jiān)控redis節(jié)點(diǎn)是否良好運(yùn)行,如果節(jié)點(diǎn)不可達(dá)就會(huì)對(duì)節(jié)點(diǎn)進(jìn)行下線標(biāo)識(shí)
2、如果被標(biāo)識(shí)的是主節(jié)點(diǎn),哨兵就會(huì)選舉一個(gè)redis從(slave)節(jié)點(diǎn)成為新的主節(jié)點(diǎn)繼續(xù)對(duì)外提供讀寫服務(wù), 進(jìn)而實(shí)現(xiàn)自動(dòng)故障轉(zhuǎn)移,保證系統(tǒng)的高可用。
3、在redis主節(jié)點(diǎn) 和 從節(jié)點(diǎn) 進(jìn)行切換后,主節(jié)點(diǎn)配置文件master_redis.conf、從節(jié)點(diǎn)配置文件slave_redis.conf都要發(fā)生改變。
2. 準(zhǔn)備工作:
- Redis集群推薦一主兩從,共三個(gè)節(jié)點(diǎn)。
- jedis-2.9.0.jar 客戶端框架
3. 代碼實(shí)現(xiàn)
JavaSentinel.java
package com.middleware.redis.sentinels; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.*; /** ?* java版哨兵 ?*? ?* @author 93733 ?* ?*/ public class JavaSentinel { ?? ?// 主節(jié)點(diǎn)ip:端口 ? ?127.0.0.1:6379 ?? ?static String masterAddress = "127.0.0.1:6379"; ?? ?// 所有 slave ?? ?static final Vector<String> slaveRedisServers = new Vector<String>(); ?? ?// 壞掉的實(shí)例 ?? ?static final Vector<String> badRedisServers = new Vector<String>(); ?? ?// 連接池對(duì)象 ?? ?static JedisPool jedisPool ; ?? ?// 連接池配置信息對(duì)象 ? ? private static JedisPoolConfig config = new JedisPoolConfig(); ?? ?/** ?? ? * 配置連接池信息 ?? ? * @return ?? ? */ ?? ?static { ?? ??? ?// 最大連接數(shù)10 ?? ??? ?config.setMaxTotal(10); ?? ??? ?//最大空閑連接數(shù)5 ?? ??? ?config.setMaxIdle(5); ?? ?} ?? ?/** ?? ? * 獲取jedis 實(shí)例 ?? ? * @param ?? ? * @return ?? ? */ ?? ?public Jedis newJedisInstance() { ?? ??? ?return jedisPool.getResource() ; ?? ?} ?? ?volatile static JavaSentinel javaSentinel; ?? ?/** ?? ? * 創(chuàng)建JavaSentinel對(duì)象 ?? ? * @param isOpenSentinel 是否開啟哨兵 true 開啟, false 不開啟 ?? ? * @return ?? ? * ?? ? * 1) 如果開啟哨兵, 我們創(chuàng)建一個(gè)定時(shí)任務(wù), 延遲1秒,間隔3秒執(zhí)行一次 ?? ? * 2)每次執(zhí)行時(shí), 任務(wù)如下: ?? ? * ? ? ? ? ? ? ?// 檢測(cè) master是否可以 ?? ? * ?? ??? ??? ??? ?checkMaster(); ?? ? * ?? ??? ??? ??? ?// 更新slave列表 ?? ? * ?? ??? ??? ??? ?updateSlaves(); ?? ? * ?? ??? ??? ??? ?// 檢測(cè)壞掉的實(shí)例是否恢復(fù)正常 ?? ? * ?? ??? ??? ??? ?checkBadServer(); ?? ? * ?? ? * 3)初始化 jedisPool 對(duì)象 和 javaSentinel對(duì)象 ?? ? * ?? ? */ ?? ?public static synchronized JavaSentinel getInstance(boolean isOpenSentinel){ ?? ??? ?// 是否開啟java哨兵 ?? ??? ?if(isOpenSentinel){ ?? ??? ??? ?// 定時(shí)任務(wù) ?? ??? ??? ?new Timer().schedule(new TimerTask() { ?? ??? ??? ??? ?@Override ?? ??? ??? ??? ?public void run() { ?? ??? ??? ??? ??? ?// 檢測(cè) master是否可以 ?? ??? ??? ??? ??? ?checkMaster(); ?? ??? ??? ??? ??? ?// 更新slave列表 ?? ??? ??? ??? ??? ?updateSlaves(); ?? ??? ??? ??? ??? ?// 檢測(cè)壞掉的實(shí)例是否恢復(fù)正常 ?? ??? ??? ??? ??? ?checkBadServer(); ?? ??? ??? ??? ?} ?? ??? ??? ?}, 1000, 3000); ?? ??? ?} ?? ??? ?if(null == javaSentinel){ ?? ??? ??? ?/** ?? ??? ??? ? * 初始化redis連接池對(duì)象 ?? ??? ??? ? */ ?? ??? ??? ?String[] serverInfo = masterAddress.split(":"); ?? ??? ??? ?String masterHost = serverInfo[0] ; ?? ??? ??? ?int masterPort = Integer.parseInt(serverInfo[1]) ; ?? ??? ??? ?jedisPool = new JedisPool(config, masterHost, masterPort, 100000); ?? ??? ??? ?//初始化當(dāng)前類對(duì)象 ?? ??? ??? ?javaSentinel = new JavaSentinel(); ?? ??? ?} ?? ??? ?return javaSentinel; ?? ?} ?? ?/** ?? ? * 該方法通過ping 方式, 檢驗(yàn)當(dāng)前redis主節(jié)點(diǎn)是否在線 ?? ? * ?? ? * 如若發(fā)生異常, 則主節(jié)點(diǎn)掛掉, 需要做如下兩步: ?? ? * 1)如果捕獲到了異常證明: ?redis節(jié)點(diǎn)掛掉, 我們需要將當(dāng)前主節(jié)點(diǎn)address保存到badRedisServers集合中 ?? ? * 2)調(diào)用changeMaster() 方法,選舉從節(jié)點(diǎn)作為新的主 ?? ? */ ?? ?private static void checkMaster() { ?? ??? ?// 主從切換 ?? ??? ?// 檢查狀態(tài) ?? ??? ?System.out.println("檢查master狀態(tài):" + masterAddress); ?? ??? ?String masterHost = masterAddress.split(":")[0]; ?? ??? ?int masterPort = Integer.parseInt(masterAddress.split(":")[1]); ?? ??? ?try { ?? ??? ??? ?Jedis jedis = new Jedis(masterHost, masterPort); ?? ??? ??? ?jedis.ping(); ?? ??? ??? ?jedis.close(); ?? ??? ?} catch (Exception e) { ?? ??? ??? ?// master掛掉啦 ?? ??? ??? ?badRedisServers.add(masterAddress); ?? ??? ??? ?// 切換master ?? ??? ??? ?changeMaster(); ?? ??? ?} ?? ?} ?? ?/** ?? ? * 切換master ?? ? * ?? ? * 1) 從slaveRedisServers集合中, 獲取一個(gè)從節(jié)點(diǎn)地址 ?? ? * 2)通過地址創(chuàng)建jedis對(duì)象嘗試ping動(dòng)作,驗(yàn)證器是否在線 ?? ? * 3)沒發(fā)生異常,證明在線,我們需要禁用它從死掉master繼續(xù)同步數(shù)據(jù) ?? ? * 4)修改屬性masterAddress 為新選舉出來的slave地址 ?? ? * 5)如果發(fā)生異常,則將當(dāng)前slave存放在badRedisServers集合中, 進(jìn)入下一次循環(huán)重試1-4 動(dòng)作 ?? ? * 6)選舉成功后,將當(dāng)前slave從 slaveRedisServers集合中移除掉 ?? ? * ?? ? * 7)遍歷slaveRedisServers集合,將其他從節(jié)點(diǎn) 主從復(fù)制配置更新到剛剛選舉出來的新主節(jié)點(diǎn)身上 ?? ? */ ?? ?private static void changeMaster() { ?? ??? ?Iterator<String> iterator = slaveRedisServers.iterator(); ?? ??? ?while (iterator.hasNext()) { ?? ??? ??? ?String slaveAddress = iterator.next(); ?? ??? ??? ?try { ?? ??? ??? ??? ?String slaveHost = slaveAddress.split(":")[0]; ?? ??? ??? ??? ?int slavePort = Integer.parseInt(slaveAddress.split(":")[1]); ?? ??? ??? ??? ?Jedis jedis = new Jedis(slaveHost, slavePort); ?? ??? ??? ??? ?/*確保當(dāng)前從節(jié)點(diǎn)在線*/ ?? ??? ??? ??? ?jedis.ping(); ? ? ? ? ? ? ? ? /*禁用當(dāng)前從節(jié)點(diǎn)同步復(fù)制*/ ?? ??? ??? ??? ?jedis.slaveofNoOne(); ?? ??? ??? ??? ?jedis.close(); ?? ??? ??? ??? ?masterAddress = slaveAddress; ?? ??? ??? ??? ?System.out.println("產(chǎn)生新的master:" + masterAddress); ?? ??? ??? ??? ?break; ?? ??? ??? ?} catch (Exception e) { ?? ??? ??? ??? ?badRedisServers.add(slaveAddress); ?? ??? ??? ?} finally { ?? ??? ??? ??? ?iterator.remove(); ?? ??? ??? ?} ?? ??? ?} ?? ??? ?// 所有slave切到新的master ?? ??? ?for (String slave : slaveRedisServers) { ?? ??? ??? ?String slaveHost = slave.split(":")[0]; ?? ??? ??? ?int slavePort = Integer.parseInt(slave.split(":")[1]); ?? ??? ??? ?Jedis jedis = new Jedis(slaveHost, slavePort); ?? ??? ??? ?jedis.slaveof(masterAddress.split(":")[0], Integer.parseInt(masterAddress.split(":")[1])); ?? ??? ??? ?jedis.close(); ?? ??? ?} ?? ?} ?? ?/** ?? ? * 更新當(dāng)前所有從節(jié)點(diǎn)到 slaveRedisServers中 ?? ? * ?? ? * 1)根據(jù)masterAddress 創(chuàng)建主節(jié)點(diǎn)Jedis對(duì)象 ?? ? * 2)獲取主節(jié)點(diǎn)replication配置信息jedis.info("replication"); ?? ? * 3)根據(jù)配置信息, 獲取到當(dāng)前主節(jié)點(diǎn)從節(jié)點(diǎn)個(gè)數(shù) ?? ? * 4)循環(huán)遍歷從節(jié)點(diǎn)個(gè)數(shù), 如果個(gè)數(shù)大于0, 則清空當(dāng)前 slaveRedisServers集合 ?? ? * 5)從配置信息中截取出所有從節(jié)點(diǎn)的ip:端口后,放入到 slaveRedisServers集合中 ?? ? * ?? ? */ ?? ?private static void updateSlaves() { ?? ??? ?// 獲取所有slave ?? ??? ?try { ?? ??? ??? ?String masterHost = masterAddress.split(":")[0]; ?? ??? ??? ?int masterPort = Integer.parseInt(masterAddress.split(":")[1]); ?? ??? ??? ?Jedis jedis = new Jedis(masterHost, masterPort); ?? ??? ??? ?String info_replication = jedis.info("replication"); ?? ??? ??? ?// 解析info replication ?? ??? ??? ?String[] lines = info_replication.split("\r\n"); ?? ??? ??? ?int slaveCount = Integer.parseInt(lines[2].split(":")[1]); ?? ??? ??? ?if (slaveCount > 0) { ?? ??? ??? ??? ?slaveRedisServers.clear(); ?? ??? ??? ??? ?for (int i = 0; i < slaveCount; i++) { ?? ??? ??? ??? ??? ?String host = lines[3 + i].split(",")[0].split("=")[1]; ?? ??? ??? ??? ??? ?String port = lines[3 + i].split(",")[1].split("=")[1]; ?? ??? ??? ??? ??? ?slaveRedisServers.add(host + ":" + port); ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ??? ?System.out.println("更新slave列表:" + Arrays.toString(slaveRedisServers.toArray(new String[] {}))); ?? ??? ??? ?jedis.close(); ?? ??? ?} catch (Exception e) { ?? ??? ??? ?e.printStackTrace(); ?? ??? ??? ?System.out.println("更新slave失敗:" + e.getMessage()); ?? ??? ?} ?? ?} ?? ?/** ?? ? * 檢測(cè)壞掉的實(shí)例是否恢復(fù)正常 ?? ? * 1)如果調(diào)用 pint() 沒有發(fā)生異常, 證明恢復(fù)正常 ?? ? * 2)恢復(fù)正常后,先將當(dāng)前節(jié)點(diǎn)主從復(fù)制的配置通過slaveof() 掛載當(dāng)前節(jié)點(diǎn)上 ?? ? * 3)將當(dāng)前節(jié)點(diǎn)地址從 badRedisServers集合中remove()掉, 并添加到 slaveRedisServers集合中。 ?? ? *? ?? ? */ ?? ?private static void checkBadServer() { ?? ??? ?// 獲取所有slave ?? ??? ?Iterator<String> iterator = badRedisServers.iterator(); ?? ??? ?while (iterator.hasNext()) { ?? ??? ??? ?String bad = iterator.next(); ?? ??? ??? ?try { ?? ??? ??? ??? ?String badHost = bad.split(":")[0]; ?? ??? ??? ??? ?int badPort = Integer.parseInt(bad.split(":")[1]); ?? ??? ??? ??? ?Jedis badServer = new Jedis(badHost, badPort); ?? ??? ??? ??? ?badServer.ping(); ?? ??? ??? ??? ?// 如果ping沒有問題,則掛在當(dāng)前的master ?? ??? ??? ??? ?badServer.slaveof(masterAddress.split(":")[0], Integer.parseInt(masterAddress.split(":")[1])); ?? ??? ??? ??? ?badServer.close(); ?? ??? ??? ??? ?slaveRedisServers.add(bad); ?? ??? ??? ??? ?iterator.remove(); ?? ??? ??? ??? ?System.out.println(bad + " 恢復(fù)正常,當(dāng)前master:" + masterAddress); ?? ??? ??? ?} catch (Exception e) { ?? ??? ??? ?} ?? ??? ?} ?? ?} }
到此這篇關(guān)于Java實(shí)現(xiàn)Redis哨兵的示例代碼的文章就介紹到這了,更多相關(guān)Java Redis哨兵內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Hadoop運(yùn)行時(shí)遇到j(luò)ava.io.FileNotFoundException錯(cuò)誤的解決方法
今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著Hadoop運(yùn)行時(shí)遇到j(luò)ava.io.FileNotFoundException錯(cuò)誤展開,文中有非常詳細(xì)的解決方法,需要的朋友可以參考下2021-06-06Java日期轉(zhuǎn)換注解配置date?format時(shí)間失效
這篇文章主要為大家介紹了Java日期轉(zhuǎn)換注解配置date?format時(shí)間失效,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java中HashMap與String字符串互轉(zhuǎn)的問題解決
本文介紹了Java中HashMap與String字符串互轉(zhuǎn)的問題解決,當(dāng)我們有需求將HashMap轉(zhuǎn)為Json格式的String時(shí),需要使用FastJson/Gson將HashMap轉(zhuǎn)為String,感興趣的可以了解一下2022-03-03Java實(shí)現(xiàn)替換Word中文本和圖片功能
Word中的替換功能以查找指定文本然后替換為新的文本,可單個(gè)替換或全部替換。本文將用Java語言實(shí)現(xiàn)Word中的文本、圖片替換功能,需要的可以參考一下2022-06-06JAVA時(shí)間存儲(chǔ)類Period和Duration使用詳解
這篇文章主要為大家介紹了JAVA時(shí)間存儲(chǔ)類Period和Duration使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09SpringBoot如何通過@Profile注解配置多環(huán)境
在Spring中,可以使用配置文件的方式來指定不同環(huán)境下所需要的配置信息,本文給大家介紹SpringBoot如何通過@Profile注解配置多環(huán)境,感興趣的朋友跟隨小編一起看看吧2023-06-06