Redis單機搭建主從(1)
阿新 • • 發佈:2018-12-25
Redis單機主從切換部署說明
準備工作
redis.io下載部署包 eg:redis-3.2.8.tar.gz
新建主從目錄
mkdir -p /usr/local/redis/master/
mkdir -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客戶端連線下執行:
172.20.1.47:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.20.1.47,port=6380,state=online,offset=38335325,lag=0
master_repl_offset:38335461
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:37286886
repl_backlog_histlen:1048576
- slave_redis客戶端連線下執行:
172.20.1.47:6380> info replication
# Replicatio
role:slave
master_host:172.20.1.47
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:38343419
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
測試主從複製,讀寫分離
- master_redis客戶端連線下執行:
set name zhangsan
- master_redis客戶端連線下執行:
get name
結果: zhangsan - slave_redis客戶端連線下執行:
get name
結果: zhangsan - slave_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
配置主從切換
- kill掉當前所有redis程序
ps -ef | grep redis
kill -9 pid
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();
}
}
}