Redis: sentinel集羣的搭建和測試


Sentinel集羣的搭建和測試

1.1    測試環境

    master:     127.0.0.16379

   slave1:     127.0.0.1 6380

   slave2:     127.0.0.1 6381

   master-sentinel: 127.0.0.1  26379

   slave1-sentinel: 127.0.0.1  26380

   slave2-sentinel: 127.0.0.1  26381


1.2   集羣配置

1)   Master

redis.conf:
daemonize yes
port 6379
requirepass pw       
masterauth  pw
appendonly no
save ""
slave-read-only yes

6379-sentinal.conf
port 26379
sentinel monitor mymaster 192.168.0.105 6379 2
sentinel auth-pass mymaster foobared
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 900000


2)   Salve1

Redis.conf
daemonize yes
port 6380
requirepass pw       
masterauth  pw
appendonly no
save ""
slave-read-only yes
slaveof 192.168.0.105 6379
6380-sentinal.conf
port 26380
sentinel monitormymaster 192.168.0.105 6379 2
sentinel auth-passmymaster pw
sentineldown-after-milliseconds mymaster 30000
sentinel parallel-syncsmymaster 1
sentinel failover-timeout mymaster 900000

3)    Salve2

Redis.conf
daemonize yes
port 6381
requirepass pw       
masterauth  pw
appendonly no
save ""
slave-read-only yes
slaveof 192.168.0.105 6379
6381-sentinal.conf
port 26381
sentinel monitormymaster 192.168.0.105 6379 2
sentinel auth-passmymaster pw
sentineldown-after-milliseconds mymaster 30000
sentinel parallel-syncsmymaster 1
sentinel failover-timeout mymaster 900000

1.3        啓動集羣

寫一個批量啓動集羣的shell腳本:

St-batch-start.sh

./bin/redis-server./st_cluster_1/master-6379/redis.conf&
./bin/redis-sentinel./st_cluster_1/master-6379/6379-sentinal.conf&
./bin/redis-server./st_cluster_1/salve-6380/redis.conf&
./bin/redis-sentinel ./st_cluster_1/salve-6380/6380-sentinal.conf&
./bin/redis-server./st_cluster_1/salve-6381/redis.conf&
./bin/redis-sentinel./st_cluster_1/salve-6381/6381-sentinal.conf&

#sh st-batch-start.sh

 

寫一個批量關閉集羣的shell腳本:

St-batch-stop.sh

kill -9 `ps -ef | grep redis | awk '{print $2}'`

#sh st-batch-stop.sh


1.1   啓動後的結果


起一個會話查看master的情況:

redis-cli -h 192.168.0.105 -p 6379
auth pw
info Replication



起一個會話查詢salve1的情況:

redis-cli -h192.168.0.105 -p 6380


起一個會話查詢salve2的情況:

redis-cli -h192.168.0.105 -p 6381



此時查看sentinal各個主機的配置文件:

端口爲26379的sentinel的配置文件沒有改變:



端口爲26380的sentinel的配置文件有改變:



端口爲26381的文件被重寫了,因爲它們發現了master的salve 6381,和另一個sentinel 26381。


1.4        場景測試

 ----場景1:master宕機

master-sentinel作爲master 1的leader,會選取一個master 1的slave作爲新的master。slave的選取是根據一個判斷DNS情況的優先級來得到,優先級相同通過runid的排序得到,但目前優先級設定還沒實現,所以直接獲取runid排序得到slave 1。然後發送命令slaveofno one來取消slave 1的slave狀態來轉換爲master。當其他sentinel觀察到該slave成爲master後,就知道錯誤處理例程啓動了。sentinel A然後發送給其他slave slaveof new-slave-ip-port 命令,當所有slave都配置完後,sentinelA從監測的masters列表中刪除故障master,然後通知其他sentinels。

    關閉master:

    現在6379是master,6380和6381是salve。

    

Sentinel底下觀察選舉新的master的過程:

顯示了failover的過程:


選擇6380爲master:


----場景2:master恢復

重新啓動原來的master:

./bin/redis-server./st_cluster_1/master-6379/redis.conf&



查看sentinel狀態:


原來的master自動切換成slave,不會自動恢復成master。


----場景3salve1宕機

接着上面的繼續實驗。

關閉slave16379


查看masterReplication信息:


此時只存在一個slave

----場景4:salve1重啓

./bin/redis-server./st_cluster_1/master-6379/redis.conf&

查看sentinel狀態:

sentinel能快速的發現slave加入到集羣中:


查看master的Replication信息:



1.5   Java客戶端測試

修改Jedis 2.8的test case進行測試。

package redis.clients.jedis.tests;

import java.util.HashSet;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.tests.utils.JedisSentinelTestUtil;

public class JedisSentinelPoolTest extends JedisTestBase {
  private static final String MASTER_NAME = "mymaster";
  private static final String localHost = "192.168.0.106";

  protected static HostAndPort master = new HostAndPort(localHost, 6379);
  protected static HostAndPort slave1 = new HostAndPort(localHost, 6380);
  protected static HostAndPort slave2 = new HostAndPort(localHost, 6381);
  
 
  protected static HostAndPort sentinel1 = new HostAndPort(localHost, 26379);
  protected static HostAndPort sentinel2 = new HostAndPort(localHost, 26380);
  protected static HostAndPort sentinel3 = new HostAndPort(localHost, 26381);
  

  protected static Jedis sentinelJedis1;
  protected static Jedis sentinelJedis2;
  protected static Jedis sentinelJedis3;

  protected Set<String> sentinels = new HashSet<String>();

  @Before
  public void setUp() throws Exception {
    sentinels.add(sentinel1.toString());
    sentinels.add(sentinel2.toString());
    sentinels.add(sentinel3.toString());

    sentinelJedis1 = new Jedis(sentinel1.getHost(), sentinel1.getPort());
    sentinelJedis2 = new Jedis(sentinel2.getHost(), sentinel2.getPort());
    sentinelJedis3 = new Jedis(sentinel3.getHost(), sentinel3.getPort());
  }

  @Test(expected = JedisConnectionException.class)
  public void initializeWithNotAvailableSentinelsShouldThrowException() {
    Set<String> wrongSentinels = new HashSet<String>();
    wrongSentinels.add(new HostAndPort(localHost, 65432).toString());
    wrongSentinels.add(new HostAndPort(localHost, 65431).toString());

    JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, wrongSentinels);
    pool.destroy();
  }

  
  @Test(expected = JedisException.class)
  public void initializeWithNotMonitoredMasterNameShouldThrowException() {
    final String wrongMasterName = "wrongMasterName";
    JedisSentinelPool pool = new JedisSentinelPool(wrongMasterName, sentinels);
    pool.destroy();
  }
  

  @Test
  public void checkCloseableConnections() throws Exception {
    GenericObjectPoolConfig config = new GenericObjectPoolConfig();

    JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 10000,
        "pw", 3);
    Jedis jedis = pool.getResource();
    jedis.auth("pw");
    jedis.set("foo", "bar"); 
    String ss = jedis.get("test");
    assertEquals("bar", jedis.get("foo"));
    jedis.close();
    pool.close();
    assertTrue(pool.isClosed());
  }

 
  @Test
  public void ensureSafeTwiceFailover() throws InterruptedException {
    JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels,
        new GenericObjectPoolConfig(), 9000, "pw", 3);

    forceFailover(pool);
    // after failover sentinel needs a bit of time to stabilize before a new
    // failover
    Thread.sleep(100);
    forceFailover(pool);

    // you can test failover as much as possible
  }
  
  @Test
  public void returnResourceShouldResetState() {
    GenericObjectPoolConfig config = new GenericObjectPoolConfig();
    config.setMaxTotal(1);
    config.setBlockWhenExhausted(false);
    JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000,
        "pw", 3);

    Jedis jedis = pool.getResource();
    Jedis jedis2 = null;

    try {
      jedis.set("hello", "jedis");
      Transaction t = jedis.multi();
      t.set("hello", "world");
      jedis.close();

      jedis2 = pool.getResource();

      assertTrue(jedis == jedis2);
      assertEquals("jedis", jedis2.get("hello"));
    } catch (JedisConnectionException e) {
      if (jedis2 != null) {
        jedis2 = null;
      }
    } finally {
      jedis2.close();

      pool.destroy();
    }
  }

  @Test
  public void checkResourceIsCloseable() {
    GenericObjectPoolConfig config = new GenericObjectPoolConfig();
    config.setMaxTotal(1);
    config.setBlockWhenExhausted(false);
    JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000,
        "pw", 3);

    Jedis jedis = pool.getResource();
    try {
      jedis.set("hello", "jedis");
    } finally {
      jedis.close();
    }

    Jedis jedis2 = pool.getResource();
    try {
      assertEquals(jedis, jedis2);
    } finally {
      jedis2.close();
    }
  }

  private void forceFailover(JedisSentinelPool pool) throws InterruptedException {
    HostAndPort oldMaster = pool.getCurrentHostMaster();

    // jedis connection should be master
    Jedis beforeFailoverJedis = pool.getResource();
    assertEquals("PONG", beforeFailoverJedis.ping());

    waitForFailover(pool, oldMaster);

    Jedis afterFailoverJedis = pool.getResource();
    assertEquals("PONG", afterFailoverJedis.ping());
    assertEquals("pw", afterFailoverJedis.configGet("requirepass").get(1));
    assertEquals(3, afterFailoverJedis.getDB());

    // returning both connections to the pool should not throw
    beforeFailoverJedis.close();
    afterFailoverJedis.close();
  }

  private void waitForFailover(JedisSentinelPool pool, HostAndPort oldMaster)
      throws InterruptedException {
    HostAndPort newMaster = JedisSentinelTestUtil.waitForNewPromotedMaster(MASTER_NAME,
      sentinelJedis1, sentinelJedis2);

    waitForJedisSentinelPoolRecognizeNewMaster(pool, newMaster);
  }

  private void waitForJedisSentinelPoolRecognizeNewMaster(JedisSentinelPool pool,
      HostAndPort newMaster) throws InterruptedException {

    while (true) {
      HostAndPort currentHostMaster = pool.getCurrentHostMaster();

      if (newMaster.equals(currentHostMaster)) break;

      System.out.println("JedisSentinelPool's master is not yet changed, sleep...");

      Thread.sleep(100);
    }
  }

}



發佈了69 篇原創文章 · 獲贊 11 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章