1. 程式人生 > >金庸武功之“獨孤九劍”--redis一主三從配置

金庸武功之“獨孤九劍”--redis一主三從配置

刪掉 tab 保留 得到 第一步 127.0.0.1 tcp http bsp

redis-server說明

服務器A:192.168.1.131:8000(主)

服務器B:192.168.1.135:8000

服務器C:192.168.1.231:8000

服務器D:192.168.1.241:8000

redis-sentinel說明

服務器A:192.168.1.131:6800

服務器B:192.168.1.135:6800

服務器C:192.168.1.231:6800

服務器D:192.168.1.241:6800

2.搭建redis系統

yum install -y gcc* tcl

首先下載安裝redis

cd /usr/local

wget http://download.redis.io/releases/redis-3.2.11.tar.gz

tar zxvf redis-3.2.11.tar.gz

mv redis-3.2.11 redis

cd redis

make && make install

如果編譯安裝過程中,出現報錯:

zmalloc.h:51:31: error: jemalloc/jemalloc.h: No such file or directory

原因:一些編譯依賴或原來編譯遺留出現的問題

解決方案:make distclean 清理一下,然後再make

或者直接 make MALLOC=libc && make install

[root@localhost redis]# cd src

[root@localhost src]# cp redis-server redis-cli redis-check-aof redis-check-rdb redis-sentinel redis-trib.rb /usr/local/bin/

[root@localhost src]# mkdir /etc/redis

[root@localhost src]# mkdir /var/redis

[root@localhost src]# mkdir /var/redis/{log,run,redis}

[root@localhost src] cd ..

[root@localhost redis]# cp redis.conf /etc/redis/redis.conf

[root@localhost redis]# vi /etc/redis/redis.conf

修改如下:

port 8000 #修改端口是安全的第一步

daemonize yes

bind 0.0.0.0

pidfile /var/run/redis-8000.pid

logfile /var/redis/log/redis_8000.log

dir /var/redis/redis //工作目錄,dump文件所在目錄

slaveof 192.168.1.131 8000 #從redis比主redis多這一行

########################################################

redis默認的持久化方式是RDB,數據寫入到dump文件中。如果要啟用AOF持久化,就在redis.conf文件中配置如下:

appendonly yes #啟用AOF持久化方式

appendfilename "appendonly.aof" #AOF文件的名稱,默認為appendonly.aof

# appendfsync always #每次收到寫命令就立即強制寫入磁盤,是最有保證的完全的持久化,但速度也是最慢的,一般不推薦使用。

appendfsync everysec #每秒鐘強制寫入磁盤一次,在性能和持久化方面做了很好的折中,是受推薦的方式。

# appendfsync no #完全依賴OS的寫入,一般為30秒左右一次,性能最好但是持久化最沒有保證,不被推薦。

########################################################

[root@localhost redis]# redis-server /etc/redis/redis.conf

設置開機啟動

[root@dev ~]# echo "/usr/local/bin/redis-server /etc/redis/redis.conf" >> /etc/rc.local

關閉redis服務

[root@dev ~]# redis-cli shutdown //默認是6379端口

如果端口變化可以指定端口

[root@dev ~]# redis-cli -p 8000 shutdown

########################################################

還可以通過如下方法,設置redis服務啟動腳本及開機自啟動

將redis解壓包下utils下redis啟動腳本redis_init_script拷貝至/etc/init.d/,並修改腳本名稱(也可不修改)為redis

[root@dev ~]# cp /usr/local/src/redis-stable/utils/redis_init_script /etc/init.d/redis

[root@dev ~]# chmod +x /etc/init.d/redis

修改腳本pid及conf路徑為實際路徑

[root@dev ~]# vim /etc/init.d/redis

......

REDISPORT=6379

EXEC=/usr/local/bin/redis-server

CLIEXEC=/usr/local/bin/redis-cli

PIDFILE=/var/redis/run/redis_6379.pid

CONF="/etc/redis/redis.conf"

......

這樣,就可以直接用下面的命令關閉和啟動redis服務了

[root@dev ~]# /etc/init.d/redis stop

Stopping ...

Redis stopped

[root@dev ~]# lsof -i:6379

[root@dev ~]# /etc/init.d/redis start

Starting Redis server...

[root@dev ~]# lsof -i:6379

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

redis-ser 9372 root 4u IPv4 58648940 0t0 TCP localhost:6379 (LISTEN)

設置自啟動

[root@dev ~]# chkconfig redis on

redis 服務不支持 chkconfig

這是因為沒有在啟動腳本/etc/init.d/redis裏加入redis啟動優先級信息,可添加如下紅色字體的兩行:

[root@dev ~]# vim /etc/init.d/redis

#!/bin/sh

#

# chkconfig: 2345 90 10 //註意:後面的英文空格

# description: Redis is a persistent key-value database //註意:後面的英文空格

# Simple Redis init.d script conceived to work on Linux systems

# as it does use of the /proc filesystem.

REDISPORT=6379

EXEC=/usr/local/bin/redis-server

CLIEXEC=/usr/local/bin/redis-cli

PIDFILE=/var/redis/run/redis_6379.pid

CONF="/etc/redis/redis.conf"

.......

[root@dev ~]# chkconfig redis on

[root@dev ~]# chkconfig --list|grep redis

redis 0:關閉 1:關閉 2:啟用 3:啟用 4:啟用 5:啟用 6:關閉

########################################################

驗證:

在A服務器(主)

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 8000 info replication

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 8000 info replication

# Replication

role:master

connected_slaves:3

slave0:ip=192.168.1.231,port=8000,state=online,offset=462,lag=0

slave1:ip=192.168.1.241,port=8000,state=online,offset=462,lag=1

slave2:ip=192.168.1.135,port=8000,state=online,offset=462,lag=1

master_repl_offset:462

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:461

#############################################################################

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 8000

[root@localhost ~]# 192.168.1.131:8000> get name thb (主,1.131)

[root@localhost redis]# redis-cli -h 192.168.1.135 -p 8000 (從。1.135)

192.168.1.135:8000> get name

"thb"

192.168.1.135:8000>

############################################################################

配置哨兵sentinel

[root@localhost redis]# cd /usr/local/redis

[root@localhost redis]# mkdir /etc/sentinel

[root@localhost redis]# cp -a sentinel.conf /etc/sentinel

[root@localhost redis]# vi /etc/sentinel/sentinel.conf (之前的都刪掉,就保留下面的內容)

bind 0.0.0.0

daemonize yes

port 6800

logfile /var/log/sentinel.log

pidfile /var/run/sentinel.pid

sentinel monitor master8000 192.168.1.131 8000 2

#5秒內master6800沒有響應,就認為SDOWN

sentinel down-after-milliseconds master8000 5000

sentinel failover-timeout master8000 15000

[root@localhost redis]# redis-sentinel /etc/sentinel/sentinel.conf

[root@localhost redis]# echo "/usr/local/bin/redis-sentinel /etc/sentinel/sentinel.conf" >> /etc/rc.local

四個redis-sentinel服務啟動完畢後,連接任意sentinel服務可以獲知當前主redis服務信息

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 6800 info sentinel

# Sentinel

sentinel_masters:1

sentinel_tilt:0

sentinel_running_scripts:0

sentinel_scripts_queue_length:0

sentinel_simulate_failure_flags:0

master0:name=master8000,status=ok,address=192.168.1.131:8000,slaves=3,sentinels=3

測試

1.把主redis停掉

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 8000 shutdown

[root@localhost ~]# netstat -ntpl

Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name

tcp 0 0 0.0.0.0:58566 0.0.0.0:* LISTEN 1235/rpc.statd

tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 1213/rpcbind

tcp 0 0 0.0.0.0:6800 0.0.0.0:* LISTEN 1685/redis-sentinel

tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1447/sshd

tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 1290/cupsd

tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1526/master

tcp 0 0 :::111 :::* LISTEN 1213/rpcbind

tcp 0 0 :::52913 :::* LISTEN 1235/rpc.statd

tcp 0 0 :::22 :::* LISTEN 1447/sshd

tcp 0 0 ::1:631 :::* LISTEN 1290/cupsd

tcp 0 0 ::1:25 :::* LISTEN 1526/master

2.查看redis-sentinel的監控狀態

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 6800 info sentinel

# Sentinel

sentinel_masters:1

sentinel_tilt:0

sentinel_running_scripts:0

sentinel_scripts_queue_length:0

sentinel_simulate_failure_flags:0

master0:name=master8000,status=ok,address=192.168.1.231:8000,slaves=3,sentinels=6

發現231這臺redis-server提升為主庫。

在1.231 這臺上查看

[root@session1 ~]# redis-cli -h 192.168.1.231 -p 8000 info replication

# Replication

role:master

connected_slaves:2

slave0:ip=192.168.1.241,port=8000,state=online,offset=36416,lag=1

slave1:ip=192.168.1.135,port=8000,state=online,offset=36558,lag=0

master_repl_offset:36842

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:36841

發現role已經是master了

至此,redis的高可用方案已經搭建完成。

六、客戶端程序

客戶端程序(如PHP程序)連接redis時需要ip和port,但redis-server進行故障轉移時,主redis是變化的,所以ip地址也是變化的。客戶端程序如何感知當前主redis的ip地址和端口呢?redis-sentinel提供了接口,請求任何一個sentinel,發送SENTINEL get-master-addr-by-name <master name>就能得到當前主redis的ip和port。

[root@localhost ~]# redis-cli -h 192.168.1.131 -p 6800

192.168.1.131:6800> sentinel get-master-addr-by-name master8000

1) "192.168.1.231"

2) "8000"

192.168.1.131:6800>

獲取當前主redis的ip和port

客戶端每次連接redis前,先向sentinel發送請求,獲得主redis的ip和port,然後用返回的ip和port連接redis。

這種方法的缺點是顯而易見的,每次操作redis至少需要發送兩次連接請求,第一次請求sentinel,第二次請求redis。

php請求sentinel程序代碼可參見:https://github.com/huyanping/redis-sentinel

更好的辦法是使用VIP,當然這對配置的環境有一定的要求,比如redis搭建在阿裏雲服務器上,可能不支持VIP。

VIP方案是,redis系統對外始終是同一ip地址,當redis進行故障轉移時,需要做的是將VIP從之前的redis服務器漂移到現在新的主redis服務器上。

比如:當前redis系統中主redis的ip地址是192.168.56.101,那麽VIP(192.168.56.250)指向192.168.56.101,客戶端程序用VIP(192.168.56.250)地址連接redis,實際上連接的就是當前主redis,這樣就避免了向sentinel發送請求。

當主redis宕機,進行故障轉移時,192.168.56.102這臺服務器上的redis提升為主,這時VIP(192.168.56.250)指向192.168.56.102,這樣客戶端程序不需要修改任何代碼,連接的是192.168.56.102這臺主redis。

技術分享圖片

VIP指向192.168.1.131

技術分享圖片

故障轉移後,VIP漂移指向192.168.1.231

七、漂移VIP

那麽現在的問題是,如何在進行redis故障轉移時,將VIP漂移到新的主redis服務器上。

這裏可以使用redis sentinel的一個參數client-reconfig-script,這個參數配置執行腳本,sentinel在做failover的時候會執行這個腳本,並且傳遞6個參數<master-name>、 <role>、 <state>、 <from-ip>、 <from-port>、 <to-ip> 、<to-port>,其中<to-ip>是新主redis的IP地址,可以在這個腳本裏做VIP漂移操作。

sentinel client-reconfig-script master8000 /opt/notify_master6800.sh

修改三個服務器的redis-sentinel配置文件/etc/sentinel.conf,增加上面一行。然後在/opt/目錄下創建notify_master6800.sh腳本文件,這個腳本做VIP漂移操作,內容如下:

#notify_master6800.sh腳本內容

#!/bin/bash

MASTER_IP=$6 #第六個參數是新主redis的ip地址

LOCAL_IP='192.168.1.231' #其他三個服務器上為192.168.1.131,192.168.56.135,192.168.1.241

VIP='192.168.1.251'

NETMASK='24'

INTERFACE='eth0'

if [ ${MASTER_IP} = ${LOCAL_IP} ];then

/sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE} #將VIP綁定到該服務器上

/sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE}

exit 0

else

/sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE} #將VIP從該服務器上刪除

exit 0

fi

exit 1 #如果返回1,sentinel會一直執行這個腳本


現在當前主redis是192.168.1.231,需要手動綁定VIP到該服務器上。

/sbin/ip addr add 192.168.1.251/24 dev eth0

/sbin/arping -q -c 3 -A 192.168.1.251 -I eth0

然後,去另一個服務器上通過VIP地址連接redis-server和redis-sentinel。

[root@localhost opt]# ifconfig

eth0 Link encap:Ethernet HWaddr 00:0C:29:34:43:27

inet addr:192.168.1.131 Bcast:192.168.1.255 Mask:255.255.255.0

inet6 addr: fe80::20c:29ff:fe34:4327/64 Scope:Link

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:3830849 errors:0 dropped:0 overruns:0 frame:0

TX packets:3406249 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:1000

RX bytes:444297730 (423.7 MiB) TX bytes:416136987 (396.8 MiB)

[root@localhost opt]# redis-cli -h 192.168.1.251 -p 8000 info replication

# Replication

role:master

connected_slaves:3

slave0:ip=192.168.1.241,port=8000,state=online,offset=18468111,lag=1

slave1:ip=192.168.1.135,port=8000,state=online,offset=18468111,lag=0

slave2:ip=192.168.1.131,port=8000,state=online,offset=18468111,lag=0

master_repl_offset:18468111

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:17419536

[root@localhost opt]# redis-cli -h 192.168.1.251 -p 6800 info sentinel

# Sentinel

sentinel_masters:1

sentinel_tilt:0

sentinel_running_scripts:0

sentinel_scripts_queue_length:0

sentinel_simulate_failure_flags:0

master0:name=master8000,status=ok,address=192.168.1.231:8000,slaves=3,sentinels=4

通過VIP連接redis

從上面也可以看出當前主redis是192.168.1.231。

下面關閉這臺redis服務,看看VIP是否漂移到另一臺服務器上。

[root@session1 opt]# redis-cli -h 192.168.1.231 -p 8000 shutdown

[root@session1 opt]# redis-cli -h 192.168.1.231 -p 6800 info sentinel

# Sentinel

sentinel_masters:1

sentinel_tilt:0

sentinel_running_scripts:0

sentinel_scripts_queue_length:0

sentinel_simulate_failure_flags:0

master0:name=master8000,status=ok,address=192.168.1.135:8000,slaves=3,sentinels=4

[root@localhost opt]# redis-cli -h 192.168.1.251 -p 6800 info sentinel

# Sentinel

sentinel_masters:1

sentinel_tilt:0

sentinel_running_scripts:0

sentinel_scripts_queue_length:0

sentinel_simulate_failure_flags:0

master0:name=master8000,status=ok,address=192.168.1.135:8000,slaves=3,sentinels=4

通過訪問VIP連接redis,發現VIP確實指向了192.168.1.135。

這裏要註意腳本裏的ip和網卡名稱(eth)不能錯,要不飄移不成功。


八、總結

通過上面的操作,使用redis主從 + 哨兵(sentinel)+ 漂移VIP的方案搭建了一個redis高可用系統,但這個系統保證的是單個redis實例的高可用,所以適合業務比較小的應用。如果業務比較大,並發量比較高,建議搭建redis集群,比如官方redis cluster,還有開源的codings集群。


金庸武功之“獨孤九劍”--redis一主三從配置