Redis單機主從切換部署說明
準備工作
redis.io下載部署包 :redis-3.2.8.tar.gz
新建主從目錄
make -p /usr/local/redis/master/
make -p /usr/local/redis/slave/
分別在兩個目錄下面安裝redis
tar –zxf redis-3.2.8.tar.gz
cd redis-3.2.8
make
#編譯
make test
#期望全部測試通過可能出現問題:缺少gcc或者tcl組件,使用命令
yum install gcc
或者yum install tcl
修改配置參數
修改master_redis配置
cd /usr/local/redis/master/redis-3.2.8/
vi redis.conf
bind 127.0.0.1 —-> bind 本機IP(綁定地址)
daemonize no —-> daemonize yes(不影響當前會話,啓動過程隱藏,守護進程)
protected-mode yes —> protected-mode no(關閉保護模式,其他服務器可訪問)
修改slave_redis配置
cd /usr/local/redis/slave/redis-3.2.8/
vi redis.conf
bind 127.0.0.1 —-> bind 本機IP(綁定地址)
daemonize no —-> daemonize yes(不影響當前會話,啓動過程隱藏,守護進程)
protected-mode yes —> protected-mode no(關閉保護模式,其他服務器可訪問)
port 6379 —> port 6380(修改端口)
slaveof master_redis所在機器IP 6379
pidfile /var/run/redis_ 6379.pid —-> pidfile /var/run/redis_ 6380.pid
redis以守護進程方式運行時,系統默認會把pid寫入/var/run/redis.pid,可以通過pidfile指 定pid文件
啓動master_ redis和slave_ redis並使用客戶端連接
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-server /usr/local/redis/master/redis-3.2.8/redis.conf
(加載配置文件)
./redis-cli -h IP -p 6379
(客戶端連接master_redis)
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-server /usr/local/redis/slave/redis-3.2.8/redis.conf
(加載配置文件)
./redis-cli -h IP -p 6380
(客戶端連接slave_redis)
使用 info 命令查看redis主從信息
master_redis客戶端連接下執行:
info replication
顯示:role:master connected_slaves:1
slave_redis客戶端連接下執行:
info replication
顯示:role:slave master_ link_ status:up
測試主從複製,讀寫分離
master_redis客戶端連接下執行:
set name zhangsan
master_redis客戶端連接下執行:
get name
結果: zhangsanslave_redis客戶端連接下執行:
get name
結果: zhangsanslave_redis客戶端連接下執行:
set name lisi
(error)READONLY You can`t write against a read only slave.(slave_redis只讀)
配置主從切換
準備sentinel.conf配置文件
#守護進程,隱藏啓動,不影響當前session
daemonize yes
#關閉保護模式,類似於防火牆的功能
protected-mode no
#sentinel 端口默認26379
port
#哨兵監控的主redis 的IP 和端口,會自動監控到slave
#sentinel monitor <master-name> <ip> <redis-port> <quorum>
#告訴sentinel去監聽地址爲ip:port的一個master,quorum是一個數字,指明當有多少個sentinel認爲一個master失效時,master纔算真正失效
sentinel monitor master1 IP 6379 1
#master被當前sentinel實例認定爲“失效”的間隔時間.
#sentinel down-after-milliseconds <mastername> <millseconds>
#如果當前sentinel與master直接的通訊中(不斷髮送程序包,並接受響應),在指定時間內沒有響應或者響應錯誤代碼,那麼當前sentinel就認爲master失效
sentinel down-after-milliseconds master1 5000
#當failover(故障轉移)開始後,在此時間內仍然沒有觸發任何failover操作,當前sentinel將會認爲此次failover失敗
sentinel failover-timeout master1 15000
#當新master產生時,可以同時進行slaveof到新master並進行“SYNC”(同步)的slave個數。(建議使用默認值1)
#在salve執行salveof與同步時,將會終止客戶端請求。此值較大,意味着“集羣”終止客戶端請求的時間總和和較大.
#此值較小,意味着“集羣”在故障轉移期間,多個salve向客戶端提供服務時仍然使用舊數據.
sentinel parallel-syncs master1 1
配置主從切換
ps -ef | grep redis
kill掉當前所有redis進程
rm -f /usr/local/redis/master/redis-3.2.8/sentinel.conf
rm -f /usr/local/redis/slave/redis-3.2.8/sentinel.conf
將準備好的sentinel.conf分別放置於對應目錄下面(替換剛剛刪除的兩個conf文件)
cd /usr/local/redis/slave/redis-3.2.8/
vi sentinel.conf
修改 port 26379 —> port 26380重新啓動主從redis
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-server /usr/local/redis/master/redis-3.2.8/redis.conf
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-server /usr/local/redis/slave/redis-3.2.8/redis.conf
啓動主從redis的sentinel(哨兵)
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-sentinel /usr/local/redis/master/redis-3.2.8/sentinel.conf
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-sentinel /usr/local/redis/slave/redis-3.2.8/sentienl.conf
ps -ef | grep redis
此時應該有四個進程(redis主從 + 兩個哨兵)
使用客戶端查看哨兵監控情況
使用客戶端連接兩個sentinel
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-cli -h IP 26379
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-cli -h IP 26380
使用
info sentinel
查看稍定監控詳情,顯示name=master1,status=ok,address=IP:6379(兩個哨兵共同監控master_redis)
測試主從自動切換(具體操作參考上面命令)
kill 掉master_redis服務
然後使用客戶端連接slave_redis
使用info replication 查看slave_ redis連接信息,會發現,slave_ redis已經升級爲master_ redis
再使用客戶端重新連接sentinel,使用info sentinel命令查看兩個哨兵監控信息,會發現監控地址變成了address=IP:6380
重新啓動kill 掉的master_ redis服務,啓動後客戶端連接,使用info replication命令查看,會發現role:slave (重新啓動後自動變成slave_ redis)
Java代碼實現redis主從
public class JedisUtil {
private final static String REDIS_HOST = "172.20.1.47";
private final static Integer REDIS_PORT = 6379;
private final static Integer REDIS_MaxActive = 200;
private final static Integer REDIS_MaxIdle = 1000;
private final static Integer REDIS_MaxWait = 512;
private final static Integer REDIS_ConnTimeout = 2000;
private final static Integer REDIS_RetryNum = 3;
private final static String SENTINEL_HOST_1 = "172.20.1.47:26381";
private final static String SENTINEL_HOST_2 = "172.20.1.47:26380";
private final static String CLUSTER_NAME = "master1";
/**
* 私有構造器.
*/
private JedisUtil() {
}
private static Map<String, JedisSentinelPool> maps = new HashMap<String, JedisSentinelPool>();
/**
* 獲取連接池.
*
* @return 連接池實例
*/
private static JedisSentinelPool getPool() {
String key = REDIS_HOST + ":" + REDIS_PORT;
Set<String> sentinels = new HashSet<String>();
String hostAndPort1 = SENTINEL_HOST_1;
String hostAndPort2 = SENTINEL_HOST_2;
sentinels.add(hostAndPort1);
sentinels.add(hostAndPort2);
String clusterName = CLUSTER_NAME;
JedisSentinelPool redisSentinelJedisPool = null;
if (!maps.containsKey(key)) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(REDIS_MaxActive);
config.setMaxIdle(REDIS_MaxIdle);
config.setMaxWaitMillis(REDIS_MaxWait);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
try {
/**
* 如果你遇到 java.net.SocketTimeoutException: Read timed out exception的異常信息 請嘗試在構造JedisPool的時候設置自己的超時值. JedisPool默認的超時時間是2秒(單位毫秒)
*/
redisSentinelJedisPool = new JedisSentinelPool(clusterName, sentinels, config, REDIS_ConnTimeout);
maps.put(key, redisSentinelJedisPool);
} catch (Exception e) {
e.printStackTrace();
}
} else {
redisSentinelJedisPool = maps.get(key);
}
return redisSentinelJedisPool;
}
/**
* 類級的內部類,也就是靜態的成員式內部類,該內部類的實例與外部類的實例 沒有綁定關係,而且只有被調用到時纔會裝載,從而實現了延遲加載。
*/
private static class RedisUtilHolder {
/**
* 靜態初始化器,由JVM來保證線程安全
*/
private static JedisUtil instance = new JedisUtil();
}
/**
* 當getInstance方法第一次被調用的時候,它第一次讀取 RedisUtilHolder.instance,導致RedisUtilHolder類得到初始化;而這個類在裝載並被初始化的時候,會初始化它的靜
* 態域,從而創建RedisUtil的實例,由於是靜態的域,因此只會在虛擬機裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。 這個模式的優勢在於,getInstance方法並沒有被同步,並且只是執行一個域的訪問,因此延遲初始化並沒有增加任何訪問成本。
*/
public static JedisUtil getInstance() {
return RedisUtilHolder.instance;
}
/**
* 獲取Redis實例.
*
* @return Redis工具類實例
*/
public Jedis getJedis() {
Jedis jedis = null;
int count = 0;
do {
try {
jedis = getPool().getResource();
} catch (Exception e) {
e.printStackTrace();
// 銷燬對象
if (jedis != null) {
jedis.close();
}
}
count++;
} while (jedis == null && count < REDIS_RetryNum);
return jedis;
}
/**
* 釋放redis實例到連接池.
*
* @param jedis redis實例
*/
public void closeJedis(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
}