redis

Posted xiao_pai_pai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis相关的知识,希望对你有一定的参考价值。

 

 

Redis Sentinel主从从高可用解决方案

一、Sentinel介绍:

Sentinel是Redis的高可用性(HA)解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进行下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。Redis提供的sentinel(哨兵)机制,通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决。

Sentinel组件核心功能:

1)         监控:Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

2)         提醒:当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

3)         自动故障迁移:当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器;

4)         通知:当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

二、Sentinel故障转移工作图解:

1)         图一:

 

2)         图二:

 

3)         图三:

 

4)         图四:

 

 

 

三、架构部署:

1)         环境准备:

服务器用途

OS版本

软件版本

IP地址

Redis Server 01(主)

Centos-6.8_64bit

redis-3.2.8

10.83.22.85

Redis Server 02(从)

Centos-6.8_64bit

redis-3.2.8

10.83.22.86

Redis Sentinel 01

Centos-6.8_64bit

redis-3.2.8

10.83.22.85

(与redis服务节点共用)

Redis Sentinel 02

Centos-6.8_64bit

redis-3.2.8

10.83.22.86

(与redis服务节点共用)

Redis Sentinel 03

Centos-6.8_64bit

redis-3.2.8

10.83.22.81

(只用于sentinel做选举)

 

2)         Redis主从配置(一主一从):

v   Redis Server 01(主)节点配置:

① cd /software

② wget ftp://bqjrftp:Pass123$%^@10.83.20.27:9020/software/redis/redis-3.2.8.tar.gz

③ tar -xf redis-3.2.8.tar.gz-C /usr/local/

④ cd /usr/local/ && ln -sv redis-3.2.8/ redis

⑤ cd /usr/local/redis

⑥ make

⑦ make install

⑧ vim /etc/profile.d/redis.sh

REDIS_HOME=/usr/local/redis/src

PATH=$PATH:$REDIS_HOME

export PATH

⑨ source /etc/profile.d/redis.sh

⑩  mkdir -pv /data/redis/sentinel

11  mkdir -p /var/run/redis

12  mkdir -p /var/log/redis

13  mkdir -p /var/redis/redis_6379

14 vim /etc/redis/redis_6379.conf(编辑Redis主服务配置文件)

daemonize yes

pidfile "/var/run/redis/redis_6379.pid"

port 6379

bind 0.0.0.0

timeout 0

tcp-keepalive 0

loglevel notice

logfile "/var/log/redis/redis_6379.log"

databases 16

save 900 1

save 300 10

save 60 10000

stop-writes-on-bgsave-error yes

rdbcompression yes

rdbchecksum yes

dbfilename "dump_6379.rdb"

dir "/var/redis/redis_6379"

maxmemory 4gb

slave-read-only yes

slave-serve-stale-data yes

repl-disable-tcp-nodelay no

slave-priority 100

appendonly yes

appendfilename "appendonly_6379.aof"

appendfsync everysec

no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

lua-time-limit 5000

slowlog-log-slower-than 10000

slowlog-max-len 128

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

list-max-ziplist-entries 512

list-max-ziplist-value 64

set-max-intset-entries 512

zset-max-ziplist-entries 128

zset-max-ziplist-value 64

activerehashing yes

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 32mb 8mb 60

hz 10

aof-rewrite-incremental-fsync yes

# Generated by CONFIG REWRITE

15 cd /etc/init.d

16 vim redis6379(制作Redis服务器启动脚本):

#!/bin/sh

#

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

# as it does use of the /proc filesystem.

# chkconfig: 2345 10 90

# description: redis_6379 ....

 

REDISPORT=6379

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

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

PIDFILE=/var/run/redis/redis_${REDISPORT}.pid

CONF="/etc/redis/redis_${REDISPORT}.conf"

 

case "$1" in

    start)

        if [ -f $PIDFILE ]

        then

                echo "$PIDFILE exists, process is already running or crashed"

        else

                echo "Starting Redis server..."

                $EXEC $CONF

        fi

        ;;

    stop)

        if [ ! -f $PIDFILE ]

        then

                echo "$PIDFILE does not exist, process is not running"

        else

                PID=$(cat $PIDFILE)

                echo "Stopping ..."

                $CLIEXEC -p $REDISPORT shutdown

                while [ -x /proc/${PID} ]

                do

                    echo "Waiting for Redis to shutdown ..."

                    sleep 1

                done

                echo "Redis stopped"

        fi

        ;;

 

    status)

        PID=$(cat $PIDFILE)

        if [ ! -x /proc/${PID} ]

        then

            echo ‘Redis is not running‘

        else

            echo "Redis is running ($PID)"

        fi

        ;;

    *)

        echo "Please use start or stop or status as first argument"

        ;;

esac

17 chmod +x redis && chkconfig redis  on

18 iptables -I INPUT 2 -p tcp -m state --state NEW -m tcp -m multiport --dports 6379 -j ACCEPT

19 service iptables save && iptables --line-numbers -L -n

20 service redis start

Redis Server 02(从)节点配置:

① 从节点参照上面Redis Server 01(主)节点的配置步骤,唯一不同的地方是第14步的配置文件某些地方需要调整,如下:

Redis Server 02(从),配置文件调整部分:

末尾增加:slaveof 10.83.22.85 6379

② 启动Redis Server 02(从)节点服务;

     service redis start

Redis Server 主从架构部署验证:

① Redis Server 01(主)节点:

redis-cli -p 6379 info|awk -F ":" ‘/role/{print $2}‘

master

② Redis Server 02(从)节点:

redis-cli -p 6379 info|awk -F ":" ‘/role/{print $2}‘

slave  

         

3)         Redis Sentinel配置(3个监视节点):

v   Redis Sentinel 01-03节点配置(完全相同):

① vim /etc/redis/sentinel.conf(编辑sentinel服务配置文件):

port 26379

dir "/data/redis/sentinel"

daemonize yes

protected-mode no 

 #注意这里是个坑,如果redis的配置文件redis._6379conf里面没有配置保护模式这个参数,这里的sentinel配置文件就必须配置protected-mode no关闭保护模式这个选项;如果redis_6379.con里面配置了protected-mode  yes,就需要配置redis的密码;

logfile "/var/log/redis/sentinel.log"

sentinel monitor MyMaster6379 10.83.22.85 6379 2

sentinel down-after-milliseconds MyMaster6379 2000

sentinel failover-timeout MyMaster6379 6000

sentinel config-epoch MyMaster6379 1

② mkdir -pv /data/redis/sentinel

③ cd /etc/init.d

④ vim sentinel(编辑sentinel服务启动脚本)

    #!/bin/sh

#

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

# as it does use of the /proc filesystem.

# chkconfig: 2345 10 90

# description: sentinel ....

SENTINEL_PORT=26379

EXEC=/usr/local/redis/src/redis-sentinel

PIDFILE=/var/run/redis/redis_${SENTINEL_PORT}.pid

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

start() {

   if [ -f $PIDFILE ]

   then

     echo "$PIDFILE exists, process is already running or crashed"

     else

     echo "Starting Redis server..."

     touch ${PIDFILE}

     $EXEC $CONF

   fi

}

stop() {

        if [ ! -f $PIDFILE ]

        then

                echo "$PIDFILE does not exist, process is not running"

        else

                PID=$(cat $PIDFILE)

                echo "Stopping ..."

                pkill redis-sentinel

                rm -rf ${PIDFILE}

                while [ -f ${PIDFILE} ]

                do

                    echo "Waiting for Redis_sentinel to shutdown ..."

                    sleep 1

                done

                echo "Redis_sentinel stopped"

        fi

}

status() {

        PID=$(cat $PIDFILE)

        if [ ! -x /proc/${PID} ]

        then

            echo ‘Redis_sentinel is not running‘

        else

            echo "Redis_sentinel is running"

        fi

 

}

case "$1" in

    start)

        start

        ;;

    stop)

        stop

        ;;

    status)

        status

        ;;

    restart)

        stop

        sleep 2

        start

        ;;

      *)

        echo "echo "USAGE: $0 start|stop|restart|status""

        ;;

esac

 

⑤ chmod +x /etc/init.d/sentinel && chkconfig sentinel on

⑥ iptables -I INPUT 3 -p tcp -m state --state NEW -m tcp -m multiport --dports 26379 -j ACCEPT

⑦ service iptables save && iptables --line-numbers -L -n

⑧ service sentinel start

v   Redis Sentinel系统部署验证:

               [[email protected] rc5.d]# redis-cli -h 10.83.22.86 -p 26379 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=MyMaster6379,status=ok,address=10.83.22.85:6379,slaves=1,sentinels=3

 

v   Redis Sentinel系统自动主从故障转移验证:

① 手动停止Redis Server 01 的redis服务,模拟redis master故障下线:

service redis stopt

② 任意redis sentinel节点查看监控信息,发现原来redis master节点已切换到原来的一台从节点服务器上,成为新的redis                     master服务节点(10.83.20.25),如图:

 

原来的主是10.83.22.85,现在切换到了10.83.22.86

③ 连接Redis Server 节点(10.83.22.86),发现此节点已经由原来的slave升级到了master,如图:

 

④ 重新恢复之前模拟的故障节点服务,发现此故障节点自动加入redis 主从从架构,并成为slave节点,它的master节点也自动                   切换到新的master节点上,如图:

 

 

 

4)利用Keepalived和脚本完成VIP的切换

         两台redis主从服务器分别安装keepalived组件,并且配置VIP地址,配置监控脚本。实现的最终目标为:

         1.  客户端连接redis只需要一个ip地址;

         2、 如果主redis服务器的redis出现故障,vip自动迁移到从redis服务器,并且从redis服务器升级为master;

         3、 如果主redis服务器的keepalived服务出现故障,vip自动迁移到从redis服务器,并且主redis服务器的redis进程自动停止,从redis服务器升级为master;

        4.1  安装keepalived

        使用最简单的方法yum来安装  yum -y install keepalived

        4.2  配置keepalived:

        4.2.1  主redis服务器keepalived配置:vim /etc/keepalived/keepalived.conf

global_defs {

lvs_id LVS_redis01

vrrp_mcast_group4 224.0.0.22

}

 

 

vrrp_script chk_redis {

  script "/etc/keepalived/scripts/redis_check.sh"

    weight -20

    interval 12                                 

  }

 

vrrp_instance VI_1 {

  state backup                           

  interface eth0                         

  virtual_router_id 52

  nopreempt

  priority 200   

  advert_int 4                  

 

  virtual_ipaddress {

    10.83.22.241                     

  }

 

  track_script {

    chk_redis                   

  }

 

   notify_stop  /etc/keepalived/scripts/keepalived_stop.sh

 

}

 

4.2.2  从redis服务器keepalived配置:

global_defs {

lvs_id LVS_redis02

vrrp_mcast_group4 224.0.0.22

}

 

vrrp_script chk_redis {

  script "/etc/keepalived/scripts/redis_check.sh"

  weight -20

  interval 12                              

}

 

vrrp_instance VI_1 {

  state backup                          

  interface eth0                        

  virtual_router_id 52

  priority 100

  nopreempt

  advert_int 4                 

  virtual_ipaddress {

   10.83.22.241

}

 

  track_script {

   chk_redis                  

  }

 

   notify_stop  /etc/keepalived/scripts/keepalived_stop.sh

}

 

 4.3  keepalived的配合脚本:

mkdir /etc/keepalived/scripts/

脚本1:redis_check.sh  主要用途是用于keepalived服务运行时根据固定间隔时间执行的脚本。原理是判断本机是不是VIP地址的所有者,如果是判断redis是不是为master,如果不是master就会停止掉keepalived服务。如果本机不是VIP地址的所有者,判断本机的redis是否能够连接通,如果连接不通也停止掉keepalived服务;

vim redis_check.sh

#!/bin/bash

Vip=10.83.22.241

Port=6379

VipValue=`/sbin/ip add|grep $Vip`

RedisCli_Cmd="/usr/local/redis/src/redis-cli"

 

case_value(){

  case $1 in

     RedisRole)

        Value=`$RedisCli_Cmd -p $Port info|awk -F‘[:| ]‘ ‘/role/{print $2}‘`

        ;;

     Alive)

        Value=`$RedisCli_Cmd -p $Port PING`

  esac

}

 

sub_value(){

    case_value $1

    if [ $Value = "$2" ];then

    exit 0

    else

          `which pkill` keepalived

          exit 1

     fi

}

 

if [ -n "$VipValue" ];then

   while true

   do

   Value=`$RedisCli_Cmd -p $Port info|awk -F‘[:| ]‘ ‘/role/{print $2}‘`

      if [ "$Value" == "slave" ];then

      sleep 1

      echo "the host redis is $Value"

      else

      break

      fi

   done

 

  sub_value RedisRole master

else

  sub_value Alive PONG

fi

 

脚本2:keepalived的停止脚本,配合keepalived配置文件一起来进行。如果keepalived服务被停止掉的话,就会同步执行停止redis服务的命令。保证vip漂移到另外一台redis机器上的同时redis的master角色也会漂移过去;

vim keepalived_stop.sh

 #!/bin/bash

`which pkill` redis-server

 

脚本3:keepalived的启动脚本,用于/etc/rc.d/rc.local开机启动。在 /etc/rc.d/rc.local里面先启动redis,然后再启动keepalived.启动keepalived的时候,先检查本机的redis是不是master,如果是master就启动,如果不是master是slave,再判断vip地址是不是能够ping通,如果能够ping通再启动keepalived

vim keepalived_start.sh

 #!/bin/bash

 

RedisRole=`/usr/local/bin/redis-cli -p 6379 info|awk -F‘[:| ]‘ ‘/role/{print $2}‘`

KeepalivedStartCmd="/etc/init.d/keepalived start"

 

if [ $RedisRole = "master" ];then

        $KeepalivedStartCmd

else

        while true;

        do

                sleep 1

                ping 10.83.22.241 -c 1 >/dev/null 2>&1

                if [ $? -eq 0 ];then $KeepalivedStartCmd;break;fi

        done

fi

      

附录:

  Redis服务配置解释:

 

*save <seconds> <changes>
save 900 1 
save 300 10
save 60 10000
这段配置的内容表示:三个策略满足其中任意一个均会触发SNAPSHOTTING操作;900s内至少有一个key有变化 
300s内至少有10个key有变化,60s内至少有1万个key发生变化;
stop-writes-on-bgsave-error yes dump操作出现错误时,是否禁止新写入操作请求;
rdbcompression yes  rdbchecksum yes 
dbfilename dump.rdb: 指定rdb文件名 
*dir /var/lib/redis:rdb文件的存储路径
AOF相关的配置
*appendonly no 
appendfilename “appendonly.aof”
*appendfsync 
Redis supports three different modes; 
no:redis不执行主动同步操作,而是OS进行;
everysec:每秒一次;
always:每语句一次;
no-appendfsync-on-rewrite no
是否在后台执行aof重写期间不调用fsync,默认no,表示调用;
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb 
上述两个条件同时满足时,会触发重写AOF;与上次aof文件大小相比,其增长量超过100%,且大小不少于64MB; 
aof-load-truncated yes 
注意:持久机制本身不能取代备份;应该制订备份策略;对redis库定期备份;

 

Redis sentinel服务配置解释:

port 26379
sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel auth-pass <master-name> <passowrd>
<quorum>表示sentinel集群的quorum机制,即至少有quorum个sentinel节点同时判定主节点故障时, 才认为其真的故障
s_down:subjectively down 
o_down: objectively down 
sentinel down-after-milliseconds <master-name><milliseconds>
监控到指定的集群的主节点异常状态持续多久才将标记为“故障”; 
sentinel parallel-syncs <master-name> <numslaves>
指在failover过程中,能够被sentinel并行配置的从节点的数量;
sentinel failover-time <master-name> <milliseconds>
sentinel必须在此指定的时长内完成故障转移操作, 否则,将视为故障转移操作失败;
sentinel notification-scripts <master-name> <script-path>
通知脚本,此脚本被自动传递多个参数;

 

Jedis Sentinel的使用简介:

Maven依赖:

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.8.0</version>
    </dependency>
    <!-- spring-redis -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.6.4.RELEASE</version>
    </dependency>

redis的配置文件:

#redis config
redis.pass=yingjun
redis.pool.maxTotal=105
redis.pool.maxIdle=10
redis.pool.maxWaitMillis=60000
redis.pool.testOnBorrow=true
 
sentinel1.ip=192.168.78.99
sentinel1.port=63791
 
sentinel2.ip=192.168.78.99
sentinel2.port=63792

Spring的配置文件:

<!-- Redis 配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="${redis.pool.maxTotal}" />
    <property name="maxIdle" value="${redis.pool.maxIdle}" />
    <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
 
<bean id="sentinelConfiguration"
    class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
    <property name="master">
        <bean class="org.springframework.data.redis.connection.RedisNode">
            <property name="name" value="master-1"></property>
        </bean>
    </property>
    <property name="sentinels">
        <set>
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <constructor-arg name="host" value="${sentinel1.ip}"></constructor-arg>
                <constructor-arg name="port" value="${sentinel1.port}"></constructor-arg>
            </bean>
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <constructor-arg name="host" value="${sentinel2.ip}"></constructor-arg>
                <constructor-arg name="port" value="${sentinel2.port}"></constructor-arg>
            </bean>
        </set>
    </property>
</bean>
 
<!-- Jedis ConnectionFactory连接配置 -->
<bean id="jedisConnectionFactory"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="password" value="${redis.pass}"></property>
    <property name="poolConfig" >
        <ref bean="jedisPoolConfig"/>
    </property>
    <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
</bean>
 
<!-- redisTemplate配置,redisTemplate是对Jedis的对redis操作的扩展,有更多的操作,封装使操作更便捷 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>

代码中直接用redisTemplate调用:

    @Override
    public boolean add(final KeyToken tkey) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
 
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = getRedisSerializer();
                byte[] key = serializer.serialize(tkey.getIndex());
                byte[] name = serializer.serialize(tkey.getExpire_time());
                return connection.setNX(key, name);
            }
 
        });
        return result;
    }

 

 

 

 

 

 

 






































以上是关于redis的主要内容,如果未能解决你的问题,请参考以下文章

redis存储session配制方法

Redis实现分布式锁(设计模式应用实战)

Redis实现分布式锁(设计模式应用实战)

Redis缓存:java语言注释符号,附超全教程文档

spring boot 整合 redis

Redis 学习 —— 数据类型及操作