Redis——Sentinel

Sentinel——主從復(fù)制高可用方案

在使用主從復(fù)制時(shí)弄唧,我們面臨以下問(wèn)題:

  • 手動(dòng)故障轉(zhuǎn)移


    master宕機(jī)

    在發(fā)生上圖中的master宕機(jī)之后围苫,我們必須手動(dòng)替換主節(jié)點(diǎn)俄精,就像下圖:


    master宕機(jī)處理
  • 寫能力和存儲(chǔ)能力受限(采用讀寫分離時(shí))
    因此刁笙,Redis為我們提供了Sentinel架構(gòu):


    Sentinel.png

Sentinel是一個(gè)管理redis實(shí)例的工具,它可以實(shí)現(xiàn)對(duì)redis的監(jiān)控滋尉、通知玉控、自動(dòng)故障轉(zhuǎn)移。sentinel不斷地檢測(cè)redis實(shí)例是否可以正常工作狮惜,通過(guò)API向其他程序報(bào)告redis的狀態(tài)奸远,如果redis master不能工作既棺,則會(huì)自動(dòng)啟動(dòng)故障轉(zhuǎn)移進(jìn)程讽挟,將其中的一個(gè)slave提升為master懒叛,其他的slave重新設(shè)置新的master服務(wù)器。

  • Sentinel作用:
    • Master狀態(tài)檢測(cè)
    • 如果Master異常耽梅,則會(huì)進(jìn)行Master-Slave切換薛窥,將其中一個(gè)Slave作為Master,將之前的Master作為Slave
    • Master-Slave切換后眼姐,master_redis.conf诅迷、slave_redis.conf和sentinel.conf的內(nèi)容都會(huì)發(fā)生改變,即master_redis.conf中會(huì)多一行slaveof的配置众旗,sentinel.conf的監(jiān)控目標(biāo)會(huì)隨之調(diào)換
  • sentinel實(shí)現(xiàn)原理
    1. sentinel創(chuàng)建兩個(gè)連接master的 異步網(wǎng)絡(luò)連接

      • 命令連接:向master發(fā)送命令罢杉,并接受回復(fù)(默認(rèn)十秒一次通過(guò)INFO來(lái)獲取master信息)
      • 訂閱連接:訂閱master的sentinel:hello頻道(依賴于Redis的訂閱發(fā)布,因?yàn)槲覀円郧罢f(shuō)過(guò)Redis是不能保存過(guò)期發(fā)布消息的贡歧,所以滩租,slave節(jié)點(diǎn)可能錯(cuò)過(guò)master的消息,所以需要sentinel來(lái)接受這些信息提供給slave利朵,同時(shí)其他sentinel也可以獲取最新信息
    2. sentinel創(chuàng)建兩個(gè)連接slave的 異步網(wǎng)絡(luò)連接(當(dāng)發(fā)現(xiàn)新的slave出現(xiàn)時(shí))

      • 命令連接:向slave發(fā)送命令律想,并接受回復(fù)(默認(rèn)十秒一次通過(guò)INFO來(lái)獲取slave信息)
      • 訂閱連接:訂閱slave的頻道
    3. 創(chuàng)建連向其他sentinel的命令連接(用于信息交換:主觀/客觀下線檢測(cè))

      • sentinels字典:為master創(chuàng)建,用于保存自身以及其他監(jiān)視該master的sentinel節(jié)點(diǎn)信息
        不需要訂閱連接的原因:sentinel訂閱了master和slave的頻道绍弟,會(huì)接受到新sentinel的信息
    4. 下線檢測(cè):

      • sentinel默認(rèn)每秒一次向建立了命令連接的所有節(jié)點(diǎn)(包括sentinel)發(fā)送PING命令技即,用于檢測(cè)是否在線
      • 主觀下線:當(dāng)master超過(guò)down-after-milliseconds設(shè)置的時(shí)間仍然返回?zé)o效回復(fù)時(shí),這時(shí)候sentinel將master標(biāo)記為主觀下線
      • 客觀下線:在標(biāo)記為主觀下線后樟遣,改sentinel會(huì)想其他監(jiān)視該master的節(jié)點(diǎn)通過(guò)命令連接進(jìn)行詢問(wèn)而叼,當(dāng)贊同的數(shù)量達(dá)到我們?cè)O(shè)置的數(shù)量時(shí),認(rèn)為該master客觀下線豹悬。
        上面的總結(jié)一下可以認(rèn)為是sentinel的三個(gè)定時(shí)任務(wù):
      1. 每10秒每個(gè)sentinel對(duì)master和slave執(zhí)行INFO(故障轉(zhuǎn)移時(shí)改為1秒)
      • 發(fā)現(xiàn)slave節(jié)點(diǎn)
      • 確認(rèn)主從關(guān)系
      1. 每2秒每個(gè)sentinel通過(guò)節(jié)點(diǎn)的channel向所有主從節(jié)點(diǎn)發(fā)送命令
      • 交換對(duì)master節(jié)點(diǎn)的看法和自身信息(更新sentinels字典以及master節(jié)點(diǎn)實(shí)例結(jié)構(gòu))
      1. 每1秒每個(gè)sentinel對(duì)其他的sentinel和節(jié)點(diǎn)執(zhí)行ping(心跳檢測(cè))
      • 下線檢測(cè)(主觀下線判斷)
  1. 故障轉(zhuǎn)移
    在判斷master客觀下線后葵陵,選舉出的sentinel將進(jìn)行故障轉(zhuǎn)移
    1. 多個(gè)sentinel發(fā)現(xiàn)并確認(rèn)master有問(wèn)題
    2. 選舉出一個(gè)sentinel作為領(lǐng)導(dǎo)
    3. 選舉出一個(gè)slave作為master
    4. 通知其余slave成為新的master的slave
    5. 通知客戶端主從變化
    6. 等待老的master復(fù)活成為新master的slave
    • 領(lǐng)導(dǎo)者選舉
      • 每個(gè)做主觀下線的sentinel節(jié)點(diǎn)向其他sentinel節(jié)點(diǎn)發(fā)送sentinel is-master-down-by-addr 命令,要求成為領(lǐng)導(dǎo)者
      • 收到命令的sentinel節(jié)點(diǎn)如果沒(méi)有同意過(guò)其他sentinel節(jié)點(diǎn)的請(qǐng)求屿衅,將同意該請(qǐng)求埃难,否則拒絕
      • 當(dāng)票數(shù)超過(guò)sentinel節(jié)點(diǎn)半數(shù)且超過(guò)quorum,將成為領(lǐng)導(dǎo)者
      • 若此過(guò)程有多個(gè)sentinel節(jié)點(diǎn)成為領(lǐng)導(dǎo)者涤久,將等待一會(huì)重新選舉
  • sentinel備份策略
    可以參考以前的文章《Redis——持久化》
  • 部署sentinel主從配置
    我們這里是采用了三臺(tái)物理機(jī)涡尘,一共五個(gè)節(jié)點(diǎn),一主四從响迂,三個(gè)sentinel節(jié)點(diǎn)考抄。
    • 主節(jié)點(diǎn)主要配置
#開(kāi)放外網(wǎng)訪問(wèn)
bind 0.0.0.0 
#下面我們?cè)O(shè)置了密碼,所以開(kāi)啟安全模式
protected-mode yes
#開(kāi)放端口
port 6379
#設(shè)置為守護(hù)線程
daemonize yes
#pid文件
pidfile "/var/run/redis_6379.pid"
#日志文件
logfile "6379.log"
#rdb文件
dbfilename "dump-6379.rdb"
#文件路徑
dir "/var/redis/6379"
#主節(jié)點(diǎn)訪問(wèn)密碼(主節(jié)點(diǎn)也要設(shè)置是因?yàn)楣收锨袚Q后主節(jié)點(diǎn)有可能變?yōu)閺墓?jié)點(diǎn))
masterauth "password"
requirepass "password"
  • 從節(jié)點(diǎn)配置
#開(kāi)放外網(wǎng)訪問(wèn)
bind 0.0.0.0 
#下面我們?cè)O(shè)置了密碼蔗彤,所以開(kāi)啟安全模式
protected-mode yes
#開(kāi)放端口
port 6380
#設(shè)置為守護(hù)線程
daemonize yes
#pid文件
pidfile "/var/run/redis_6380.pid"
#日志文件
logfile "6379.log"
#rdb文件
dbfilename "dump-6380.rdb"
#文件路徑
dir "/var/redis/6380"
#主節(jié)點(diǎn)訪問(wèn)密碼
masterauth "password"
#該節(jié)點(diǎn)密碼
requirepass "password"
#設(shè)置主節(jié)點(diǎn) 我用的公網(wǎng)ip
slaveof 120.1.1.1 6379
#設(shè)置從節(jié)點(diǎn)只讀
slave-read-only yes

其他從節(jié)點(diǎn)類似

  • 哨兵節(jié)點(diǎn)配置
bind 0.0.0.0
port 26379
#要監(jiān)控的主節(jié)點(diǎn)名稱川梅、ip疯兼、port ,2表示將這個(gè)主服務(wù)器判斷為失效至少需要 2 個(gè) Sentinel 同意 
sentinel monitor mymaster 120.1.11.11 6379 2
# Sentinel 認(rèn)為服務(wù)器已經(jīng)斷線所需的毫秒數(shù)贫途。
sentinel down-after-milliseconds mymaster 60000
# failover過(guò)期時(shí)間吧彪,當(dāng)failover(選出新的master)開(kāi)始后,在此時(shí)間內(nèi)仍然沒(méi)有觸發(fā)任何failover操作丢早,當(dāng)前sentinel  將會(huì)認(rèn)為此次failoer失敗
sentinel failover-timeout mymaster 180000
#指定了在執(zhí)行故障轉(zhuǎn)移時(shí)姨裸, 最多可以有多少個(gè)從服務(wù)器同時(shí)對(duì)新的主服務(wù)器進(jìn)行同步, 這個(gè)數(shù)字越小怨酝, 完成故障轉(zhuǎn)移所需的時(shí)間就越長(zhǎng)傀缩。
sentinel parallel-syncs mymaster 1

其他哨兵節(jié)點(diǎn)類似,只是端口不同农猬。

  • 哨兵模式測(cè)試
    編寫一個(gè)工具類赡艰,用來(lái)獲取jedis實(shí)例
public class RedisUtil {

  /*redis-sentinel節(jié)點(diǎn)地址*/
  private static final String SENTINEL_1 = "ip:26379";
  private static final String SENTINEL_2 = "ip1.31:26380";
  private static final String SENTINEL_3 = "ip:26381";
  //sentinel節(jié)點(diǎn)地址集合
  private static final Set<String> SENTINELS = new HashSet<String>() {
    {
      add(SENTINEL_1);
      add(SENTINEL_2);
      add(SENTINEL_3);
    }
  };

  //masterName
  private static final String MASTER_NAME = "mymaster";
  //  訪問(wèn)密碼
  private static String AUTH = "password";
  private static String HOST = "ip";
  /**
   * 可用連接實(shí)例的最大數(shù)目,默認(rèn)值為8斤葱; 如果賦值為-1慷垮,則表示不限制;如果pool已經(jīng)分配了maxActive個(gè)jedis實(shí)例苦掘,則此時(shí)pool的狀態(tài)為exhausted(耗盡)换帜。
   */
  private static int MAX_ACTIVE = 1024;
  // 控制一個(gè)pool最多有多少個(gè)狀態(tài)為idle(空閑的)的jedis實(shí)例,默認(rèn)值也是8鹤啡。
  private static int MAX_IDLE = 200;
  // 等待可用連接的最大時(shí)間惯驼,單位毫秒,默認(rèn)值為-1递瑰,表示永不超時(shí)祟牲。如果超過(guò)等待時(shí)間,則直接拋出JedisConnectionException抖部;
  private static int MAX_WAIT = 10000;
  private static int TIMEOUT = 10000;
  // 在borrow一個(gè)jedis實(shí)例時(shí)说贝,是否提前進(jìn)行validate操作;如果為true慎颗,則得到的jedis實(shí)例均是可用的乡恕;
  private static boolean TEST_ON_BORROW = true;
  /*sentinelPool*/
  private static JedisSentinelPool SENTINEL_POLL = null;

  /**
   * 初始化Redis-Sentinel連接池.
   */
  static {
    try {
      // maxActive ==> maxTotal
      // maxWait ==> maxWaitMillis
      JedisPoolConfig CONFIG = new JedisPoolConfig();
      CONFIG.setMaxTotal(MAX_ACTIVE);
      CONFIG.setMaxIdle(MAX_IDLE);
      CONFIG.setMaxWaitMillis(MAX_WAIT);
      CONFIG.setTestOnBorrow(TEST_ON_BORROW);
      SENTINEL_POLL = new JedisSentinelPool(MASTER_NAME, SENTINELS, AUTH);
      System.out.println(SENTINEL_POLL.getCurrentHostMaster());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


  /**
   * 獲取Jedis實(shí)例.
   */
  public static synchronized Jedis getJedis() {
    Jedis jedis = null;
    int count = 0;
    do {
      try {
        jedis = SENTINEL_POLL.getResource();
      } catch (Exception e) {
        e.printStackTrace();
      }
      count++;
    } while (jedis == null && count < 10);
    return jedis;
  }
  /**
   * 釋放jedis資源
   */
  public static void returnResource(final Jedis jedis) {
    if (jedis != null) {
      jedis.close();
    }
  }
}

測(cè)試客戶端

public class RedisClient {

  public static void main(String[] args) {
    Jedis jedis = RedisUtil.getJedis();
    jedis.set("4", "1");
    jedis.set("5", "2");
    jedis.set("6", "3");
    System.out.println(jedis.get("4"));
    System.out.println(jedis.get("5"));
    System.out.println(jedis.get("6"));
    RedisUtil.returnResource(jedis);

  }
}

測(cè)試結(jié)果

可以看到,可以成功使用了俯萎,我們?cè)賮?lái)測(cè)試一下故障轉(zhuǎn)移是否可用
我們手動(dòng)關(guān)閉主節(jié)點(diǎn)傲宜,可以看到主節(jié)點(diǎn)已經(jīng)關(guān)閉
圖片.png

再來(lái)運(yùn)行一下程序,
故障轉(zhuǎn)移

發(fā)現(xiàn)主節(jié)點(diǎn)已經(jīng)自動(dòng)切換為其他的節(jié)點(diǎn)夫啊,最后我們?cè)賳?dòng)剛才關(guān)閉的主節(jié)點(diǎn)函卒,發(fā)現(xiàn)他已經(jīng)成為新主節(jié)點(diǎn)的從節(jié)點(diǎn)
圖片.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撇眯,隨后出現(xiàn)的幾起案子报嵌,更是在濱河造成了極大的恐慌虱咧,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锚国,死亡現(xiàn)場(chǎng)離奇詭異腕巡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)跷叉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門逸雹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人云挟,你說(shuō)我怎么就攤上這事∽剩” “怎么了园欣?”我有些...
    開(kāi)封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)休蟹。 經(jīng)常有香客問(wèn)我沸枯,道長(zhǎng),這世上最難降的妖魔是什么赂弓? 我笑而不...
    開(kāi)封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任绑榴,我火速辦了婚禮,結(jié)果婚禮上盈魁,老公的妹妹穿的比我還像新娘翔怎。我一直安慰自己,他們只是感情好杨耙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布赤套。 她就那樣靜靜地躺著,像睡著了一般珊膜。 火紅的嫁衣襯著肌膚如雪容握。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天车柠,我揣著相機(jī)與錄音剔氏,去河邊找鬼。 笑死竹祷,一個(gè)胖子當(dāng)著我的面吹牛谈跛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溶褪,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼币旧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了猿妈?” 一聲冷哼從身側(cè)響起吹菱,我...
    開(kāi)封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤巍虫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鳍刷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體占遥,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年输瓜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓦胎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尤揣,死狀恐怖搔啊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情北戏,我是刑警寧澤负芋,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站嗜愈,受9級(jí)特大地震影響旧蛾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蠕嫁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一锨天、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剃毒,春花似錦病袄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至纤壁,卻和暖如春左刽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酌媒。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工欠痴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秒咨。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓喇辽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親雨席。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菩咨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容