REDIS缓存

  1. 缓存
    1. Redis缓存概述

Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的key-value形式存储系统,它可以用作数据库、缓存和消息中间件。Redis内置了复制(replication)、LUA脚本、LRU事件驱动、事务和不同级别的磁盘持久化,并可通过Redis哨兵(Sentinel)或者集群(Cluster)提供高可用性(HA)。

    1. Redis单机模式

1.2.1 环境与安装包准备

系统环境:SUSE Linux Enterprise Server 11 (x86_64)。

下载Redis:redis是以源码发行的,先下载源码,然后在linux下编译,下载地址为http://www.redis.io/download,先到这里下载Stable稳定版,目前最新稳定版本是Redis 3.2.5,此处下载了Redis 3.2.4版本(redis-3.2.4.tar.gz)作为演示

1.2.2 解压、编译、安装redis

(1)将redis-3.2.4.tar.gz  源码包上传到linux服务,然后运行以下命令解压:

jtykf-41-85:/ # tar –xzvf redis-3.2.4.tar.gz

(2)进入到解压好的redis-3.2.4文件夹,执行./configure命令,来生成Makefile,为下一步编译做准备。

jtykf-41-85:/redis-3.2.4 # ./configure

(3)在redis-3.2.4文件夹当前目录下,执行make命令,来进行编译。

jtykf-41-85:/redis-3.2.4 # make

注:make命令需要linux上安装gvv

(4)最后在redis-3.2.4文件夹当前目录下执行make install命令,来进行安装。

jtykf-41-85:/redis-3.2.4 # make install

1.2.3 配置Redis环境

Redis文件目录结构见下图:

         其中redis.conf是redis的主要配置文件,编辑redis.conf配置文件。

redis的配置修改,主要对一些关键配置项进行修改:

(1).#bind 绑定主机,默认值127.0.0.1,注释掉,如果只想让redis服务在一个网络接口上监听,那就绑定一个IP或者多个IP

# bind:127.0.0.1(注释掉)    

(2). protected-mode是3.2之后加入的新特性 yes:处于保护模式 redis时只能通过本地来连接,改成no.

protected-mode no

(3). port指定Redis监听端口,默认端口为6379

port 6379

(4). daemonize默认情况下 redis 不是作为守护进程运行的,如果你想让它在后台运行,你就把它改成 yes

daemonize yes

(5). pidfile当redis作为守护进程运行的时候,它会把 pid 默认写到 /var/run/redis.pid 文件里面,但是你可以在这里自己制定它的文件位置

pidfile "redis_6379.pid"

(6) loglevel定义日志级别, debug (适用于开发或测试阶段), notice (适用于生产环境), warning (仅仅一些重要的消息被记录)

loglevel notice

(7)logfile, 指定日志文件的位置。若需改为其它目录(如./log/redis-running.log),则日志文件的父路径必须事先mkdir出来,否则会启动失败.

logfile "redis_6379.log"

(8) dbfilename,设置 dump 的文件位置,存储数据的文件,RBD是一个非常紧凑的文件,它保存了Redis在某个时间点上的数据集。

dbfilename "dump.rdb"

(9) dir, 工作目录, 例如上面的 dbfilename 只指定了文件名, 但是它会写入到这个目录下。这个配置项一定是个目录,而不能是文件名

dir "./

修改并保存好配置文件(redis.conf)后,然后就可以启动Redis服务 "

1.2.4 启动Redis服务

执行redis启动命令:

jtykf-41-85:/redis-3.2.4 # redis-server  redis.conf

使用redis-cli客户端验证:

tykf-41-85:/redis-3.2.4 # reids-cli  -p  6379

注:如果远程连接Redis服务可使用:redis-cli –h IP  -p PORT

可以通过设置set与get命令来测试缓存是否正常启动

1.2.4 客户端访问Redis单机模式

SPEED4J平台提供了两种访问redis服务的客户端。

首先对redis.propertied文件进行配置

######### RedisPoolConfig配置  #############

#最大连接数

redis.pool.maxTotal=200

#最大空闲数

redis.pool.maxIdle=-1

#最小空闲数

redis.pool.minIdle=-1

#最大等待时间(ms)

redis.pool.maxWaitMillis=100000

#使用连接时,检测连接是否成功

redis.pool.testOnBorrow=false

#返回连接时,检测连接是否成功

redis.pool.testOnReturn=false

 

 

########## 单台Redis服务器,确保redis服务器可读可写  ##########

#主机地址

redis.host=10.129.41.85

#端口号

redis.port=6379

#超时时间,非必要

redis.timeout=10000

#密码,非必要

#redis.password=

 

方式一:Jedis,官方推荐的Redis java客户端

平台进一步对Jedis进行了封装,代码如下:

Jedis工具类:JedisUtil.java

public class JedisUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JedisSentinelUtil.class); 

    private static JedisPool pool = null

    private static JedisUtil ru = new JedisUtil(); 

    

    public JedisUtil() {

        if (pool == null) {

            String host = PropertiesUtil.getValue("redis.properties", "redis.host");

            String port = PropertiesUtil.getValue("redis.properties", "redis.port");

           String maxTotal = PropertiesUtil.getValue("redis.properties", "redis.pool.maxTotal");

            String maxIdle = PropertiesUtil.getValue("redis.properties", "redis.pool.maxIdle");

            String maxWaitMillis = PropertiesUtil.getValue("redis.properties", "redis.pool.maxWaitMillis");

            String testOnBorrow = PropertiesUtil.getValue("redis.properties", "redis.pool.testOnBorrow");  

            JedisPoolConfig config = new JedisPoolConfig(); 

            config.setMaxTotal(Integer.valueOf(maxTotal));            

            config.setMaxIdle(Integer.valueOf(maxIdle)); 

            config.setMaxWaitMillis(Integer.valueOf(maxWaitMillis)); 

            boolean flag = true;

            if("true".equalsIgnoreCase(testOnBorrow)){

                flag = true;

            }else if("false".equalsIgnoreCase(testOnBorrow)){

                flag = false;

            }

            config.setTestOnBorrow(flag);

            pool = new JedisPool(config, host, Integer.valueOf(port), 100000); 

        } 

    } 

 

    /**

     * <p>通过key获取储存在redis中的value</p>

     * <p>并释放连接</p>

     * @param key

     * @return 成功返回value 失败返回null

     */ 

    public static String get(String key){ 

        Jedis jedis = null

        String value = null

        try

            jedis = pool.getResource(); 

            value = jedis.get(key); 

        } catch (Exception e) {    

            LOGGER.error(e.getMessage()); 

        } finally

            returnResource(pool, jedis); 

        } 

        return value;  }

………………

 

方式二:spring与redis整合,实现缓存

    通过给方法或类加注解的形式,操作redis缓存。

Spring配置文件:applicationContext.xml

<!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->

<cache:annotation-driven cache-manager="cacheManager" />

<!-- 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="minIdle" value="${redis.pool.minIdle}" />

       <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />

       <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />

       <property name="testOnReturn" value="${redis.pool.testOnReturn}" />

   </bean>

   

   <!-- 工厂实现 -->

   <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

       <constructor-arg index="0" ref="jedisPoolConfig" />

       <property name="usePool" value="false" />

       <property name="hostName" value="${redis.host}" />

       <property name="port" value="${redis.port}" />

       <property name="timeout" value="${redis.timeout}" />

       <!-- <property name="password" value="${redis.pass}"/> -->

   </bean>

  

   <!-- 模板类 -->

   <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">

       <constructor-arg name="connectionFactory" ref="jedisConnectionFactory" />

   </bean>

  

   <!-- spring自己的缓存管理器,这里定义了两个缓存位置名称 ,既注解中的value -->

   <bean class="org.springframework.cache.support.SimpleCacheManager" id="cacheManager">

       <property name="caches">

           <set>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="default" />

               </bean>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="commonCache" />

               </bean>

           </set>

       </property>

    </bean>

关于注解添加方式,请参考工具操作手册。

 

    1. Redis高可用(HA)方案---哨兵模式

 

为了提升系统的可用性,redis提供了类似于mysql的master-slave主从模式,master节点写入cache后,会自动同步到slave上。而且redis提供了sentinel(哨兵)机制,通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决。

每个sentinel会向其它sentinal、master、slave定时发送消息,以确认对方是否“活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的“主观认为宕机” Subjective Down,简称SDOWN)。若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡"(即:客观上的真正down机,Objective Down,简称ODOWN),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。

 

1.3.1 哨兵模式环境准备

主Redis服务(master node): 10.129.41.85:6379

从Redis服务(slave  node): 10.129.41.86:6379

哨兵1(sentinel node1):10.129.41.83:26379

哨兵2(sentinel node2):10.129.41.83:26380

Redis安装步骤参考前面单机模式

注:哨兵可配一个或多个

服务器架构图:

1.3.2 Redis服务主从配置

创建主Redis服务配置文件(redis_85.conf),配置内容与单机模式中的配置相同,再创建从Reids服务配置文件(redis_86.conf),配置内容与主Redis服务相似,只需要修改slaveof配置项:Slaveof 10.129.41.85 6379

另外注意 slave-read-only yes 这行,这表示slave只读不写,也是推荐设置。

1.3.3 验证主从配置

启动master/slave这二台机器上的redis,在master上加一个缓存项,如图。

然后在slave上取出该缓存项

取到了,说明master上的cache自动复制到slave节点了。

1.3.4 哨兵Sentinel配置

配置哨兵节点1:配置文件为sentinel_26379.conf,最小化的Sentinel配置项为:

port 26379

daemonize yes

protected-mode no

logfile sentinel_26379.log

dir ./

sentinel monitor mymaster 10.129.41.85 6379 1

sentinel down-after-milliseconds mymaster 30000

sentinel failover-timeout mymaster 60000

sentinel parallel-syncs mymaster 1

  1. Port:sentinel监听端口,默认是26379
  2. Daemonize:默认情况下不是作为守护进程运行的,如果你想让它在后台运行,就把它改成 yes
  3. protected-mode:处于保护模式时只能通过本地来连接,改成no。
  4. logfile:sentinel日志文件
  5. dir:指定工作目录
  6. sentinel monitor mymaster 10.129.41.85 6379 1

显示监控master节点10.129.41.85,master节点使用端口6379,最后一个数字表示投票需要的"最少法定人数",比如有10个sentinal哨兵都在监控某一个master节点,如果需要至少6个哨兵发现master挂掉后,才认为master真正down掉,那么这里就配置为6,最小配置1台master,1台slave,在二个机器上都启动sentinal的情况下,哨兵数只有2个,如果一台机器物理挂掉,只剩一个sentinal能发现该问题,所以这里配置成1;mymaster只是一个名字,可以随便起,但要保证配置中都使用同一个名字。

注意:无论设置多少个Sentinel同意才能判断一个 服务器失效,一个Sentinel都需要获得系统中多数Sentinel的支持,才能发起一次自动迁移,换句话说,在只有少数Sentinel进程正常运作的情况下,Sentinel是不能执行自动故障迁移的。

  1. sentinel down-after-milliseconds mymaster 30000

表示如果30s内mymaster没响应,就认为SDOWN

  1. sentinel failover-timeout mymaster 60000

表示如果60秒后,mysater仍没活过来,则启动failover,从剩下的slave中选一个升级为master

  1. sentinel parallel-syncs mymaster 1

表示如果master重新选出来后,其它slave节点能同时并行从新master同步缓存的台数有多少个,显然该值越大,所有slave节点完成同步切换的整体速度越快,但如果此时正好有人在访问这些slave,可能造成读取失败,影响面会更广。最保定的设置为1,只同一时间,只能有一台干这件事,这样其它slave还能继续服务,但是所有slave全部完成缓存更新同步的进程将变慢。

配置哨兵节点2:配置文件为sentinel_26380.conf,配置内容与sentinel_26380.conf基本一样,配置内容为:

port 26380

daemonize yes

protected-mode no

logfile sentinel_26380.log

dir ./

sentinel monitor mymaster 10.129.41.85 6379 1

sentinel down-after-milliseconds mymaster 30000

sentinel failover-timeout mymaster 60000

sentinel parallel-syncs mymaster 1

:一个sentinal可同时监控多个master,只要把部分配置重复多段,加以修改即可,部分配置为

sentinel monitor mymaster 10.129.41.85 6379 1

sentinel down-after-milliseconds mymaster 30000

sentinel failover-timeout mymaster 60000

sentinel parallel-syncs mymaster 1

1.3.5 启动哨兵

确保master、slave上的redis-server均已正常启动,哨兵配置文件所在位置如图

执行命令,启动两个哨兵:

jtykf-41-83:/redis-3.2.4 # redis-sentinel sentinel_26379.conf  

jtykf-41-83:/redis-3.2.4 # redis-sentinel sentinel_26380.conf

 

1.3.6 验证主从切换

master上,redis-cli -p 6379 shutdown ,手动把master停掉,观察sentinel的输出

从标注部分可以看出,master发生了迁移

注意事项发生master迁移后,如果遇到运维需要,想重启所有redis,必须最先重启“新的”master节点,否则sentinel会一直找不到master。如果想停止sentinel,可输入命令redis-cli -p port shutdown

1.3.7 客户端访问Redis哨兵模式

SPEED4J平台提供了两种访问redis哨兵模式的客户端。

首先对redis.propertied文件进行配置

################# RedisPoolConfig配置  #########################

#最大连接数

redis.pool.maxTotal=200

#最大空闲数

redis.pool.maxIdle=-1

#最小空闲数

redis.pool.minIdle=-1

#最大等待时间(ms)

redis.pool.maxWaitMillis=100000

#使用连接时,检测连接是否成功

redis.pool.testOnBorrow=false

#返回连接时,检测连接是否成功

redis.pool.testOnReturn=true

 

######### 多台Redis服务器,采用主从模式,使用哨兵sentinel进行监听  ###########

#主 redis名

redis.master.name=mymaster

#sentinel.count 哨兵个数

redis.sentinel.count=2

#sentinel_1:哨兵1

redis.sentinel_1.ip=10.129.41.83

redis.sentinel_1.port=26379

#sentinel_2:哨兵2

redis.sentinel_2.ip=10.129.41.83

redis.sentinel_2.port=26380

方式一:Jedis,官方推荐的Redis java客户端

进一步对Jedis  Sentinel操作进行了封装,代码如下:

Jedis 哨兵模式客户端工具类:JedisSentinelUtil.java

public class JedisSentinelUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JedisSentinelUtil.class); 

    private static JedisSentinelPool pool = null;

    private static JedisSentinelUtil ru = new JedisSentinelUtil(); 

    

    public JedisSentinelUtil() { 

        if (pool == null) { 

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

            String sentinelCount = PropertiesUtil.getValue("redis.properties", "redis.sentinel.count");

            String masterName = PropertiesUtil.getValue("redis.properties", "redis.master.name");

            String maxTotal = PropertiesUtil.getValue("redis.properties", "redis.pool.maxTotal");

            String maxIdle = PropertiesUtil.getValue("redis.properties", "redis.pool.maxIdle");

            String maxWaitMillis = PropertiesUtil.getValue("redis.properties", "redis.pool.maxWaitMillis");

            String testOnBorrow = PropertiesUtil.getValue("redis.properties", "redis.pool.testOnBorrow");

            int count = Integer.valueOf(sentinelCount);

            String sentinel_ip = null;

            String sentinel_port = null;

            for(int i=1;i<=count;i++){

                sentinel_ip = PropertiesUtil.getValue("redis.properties", "redis.sentinel_"+i+".ip");

                sentinel_port = PropertiesUtil.getValue("redis.properties", "redis.sentinel_"+i+".port");

                Jedis jedis = new Jedis(sentinel_ip,Integer.valueOf(sentinel_port));

                try{

                    jedis.sentinelGetMasterAddrByName(masterName);

                    sentinels.add(sentinel_ip+":"+sentinel_port);

                }catch(Exception e){

                    LogManager.info(sentinel_ip+":"+sentinel_port+" sentinel 连接 失败....");

                }

            }

            JedisPoolConfig config = new JedisPoolConfig(); 

            config.setMaxTotal(Integer.valueOf(maxTotal));    

            config.setMaxIdle(Integer.valueOf(maxIdle)); 

            config.setMaxWaitMillis(Integer.valueOf(maxWaitMillis));

            boolean flag = true;

            if("true".equalsIgnoreCase(testOnBorrow)){

                flag = true;

            }else if("false".equalsIgnoreCase(testOnBorrow)){

                flag = false;

            }

            config.setTestOnBorrow(flag);

            pool = new JedisSentinelPool(masterName,sentinels,config);

        } 

    } 

   

    /**

     * 获取当前的master

     * @return

     */

    public static HostAndPort getCurrentMaster(){

        return pool.getCurrentHostMaster();

    }

   

    /**

     * 获取当前master的ip

     * @return

     */

    public static String getMasterHost(){

        return pool.getCurrentHostMaster().getHost();

    }

   

    /**

     * 获取当前master的port

     * @return

     */

    public static Integer getMasterPort(){

        return pool.getCurrentHostMaster().getPort();

    }

   

    /**

     * <p>通过key获取储存在redis中的value</p>

     * <p>并释放连接</p>

     * @param key

     * @return 成功返回value 失败返回null

     */ 

    public static String get(String key){ 

        Jedis jedis = null

        String value = null

        try

            jedis = pool.getResource(); 

            value = jedis.get(key); 

        } catch (Exception e) { 

             

            LOGGER.error(e.getMessage()); 

        } finally

            returnResource(pool, jedis); 

        } 

        return value

    }  ……………

 

方式二:spring与redis整合,实现缓存

    通过给方法或类加注解的形式,操作redis缓存。

Spring配置文件:applicationContext.xml

<!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->

<cache:annotation-driven cache-manager="cacheManager" />

<bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">

        <property name="master">

            <bean class="org.springframework.data.redis.connection.RedisNode">

                <property name="name" value="${redis.master.name}"></property>

            </bean>

        </property>

        <property name="sentinels">

            <set>

                <bean class="org.springframework.data.redis.connection.RedisNode">

                    <constructor-arg index="0" value="${redis.sentinel_1.ip}"></constructor-arg>

                    <constructor-arg index="1" value="${redis.sentinel_1.port}"></constructor-arg>

                </bean>

                <bean class="org.springframework.data.redis.connection.RedisNode">

                    <constructor-arg index="0" value="${redis.sentinel_2.ip}"></constructor-arg>

                    <constructor-arg index="1" value="${redis.sentinel_2.port}"></constructor-arg>

                </bean>

            </set>

        </property>

   </bean>

  

   <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

        <constructor-arg ref="redisSentinelConfiguration"></constructor-arg>

        <!-- <property name="hostName" value="${redis.host}" />

        <property name="port" value="${redis.port}" />

        <property name="timeout" value="${redis.timeout}"/>

        <property name="usePool" value="false" /> -->

        <!-- <property name="password" value="${redis.pass}"/> -->

   </bean>

  

   <!-- 模板类 -->

   <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">

       <constructor-arg name="connectionFactory" ref="jedisConnectionFactory" />

   </bean>

  

   <!-- spring自己的缓存管理器,这里定义了两个缓存位置名称 ,既注解中的value -->

   <bean class="org.springframework.cache.support.SimpleCacheManager" id="cacheManager">

       <property name="caches">

           <set>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="default" />

               </bean>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="commonCache" />

               </bean>

           </set>

       </property>

    </bean>

关于注解添加方式,请参考工具操作手册

 

    1. Redis高可用(HA)方案---集群模式

1.4.1 Redis集群(cluster)的特性

1).节点自动发现

2).slave->master 选举,集群容错

3).Hot resharding:在线分片

4):集群管理:cluster xxx

5).基于配置(nodes-port.conf)的集群管理

6).ASK 转向/MOVED 转向机制

1.4.2 redis cluster 架构

1)redis-cluster架构图

 

https://i-blog.csdnimg.cn/blog_migrate/a3c4d319ca316a4d14775ab98b7bc7d2.jpeg

(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.

(2)节点的fail是通过集群中超过半数的master节点检测失效时才生效.

(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key

2) redis-cluster选举:容错

https://i-blog.csdnimg.cn/blog_migrate/a6a140960b7377eb4f471cd19124a918.jpeg

(1)选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作.

(2)什么时候整个集群不可用(cluster_state:fail)? 

a:如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,可以理解成集群的slot映射[0-16383]不完整时进入fail状态.

注:redis-3.0.0.rc1加入cluster-require-full-coverage参数,默认关闭,打开集群兼容部分失败.

 b:如果集群超过半数以上master挂掉,无论是否有slave集群进入fail状态.

注:当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误

1.4.3 redis cluster的搭建

1.4.3.1安装redis cluster

安装redis-cluster依赖:redis-cluster的依赖库在使用时有兼容问题,在reshard时会遇到各种错误,需要按指定版本安装。

确保linux环境上安装了zlib,Ruby

(1)确保系统安装zlib,否则gem install会报(no such file to load -- zlib)

安装命令:  

  1. #download:zlib-1.2.7.tar.gz  
  2. tar –xf zlib-1.2.7.tar.gz
  3. ./configure  
  4. make  
  5. make install  

(2)安装ruby:version(1.8.7)

安装命令:  

  1. # download:ruby-1.8.7.tar.gz  
  2. tar –xzvf ruby-1.8.7.tar.gz 
  3. ./configure 
  4. make  
  5. make install  

(3)安装rubygems:version(2.6.8)

安装命令:  

  1. # download:rubygems-2.6.8.tgz  
  2. tar –xzvf rubygems-2.6.8.tgz
  3. ./configure
  4. Make
  5. make install
  6. cd  /rubygems-2.6.8  
  7. ruby setup.rb  
  8. sudo cp bin/gem/usr/local/bin  

(4)安装gem-redis:version(3.0.7)

安装命令:

  1. # download地址:http://rubygems.org/gems/redis/versions/
  2. # download:redis-3.0.7.gem
  3. gem install -l /data/soft/redis-3.0.7.gem  

 

1.4.3.2配置redis cluster

为了方便演示,在一台机器上进行演示,创建六个redis服务实例

  1. redis配置文件结构

https://i-blog.csdnimg.cn/blog_migrate/39021639488b96e53a067049b664d00a.jpeg

  使用包含(include)把通用配置和特殊配置分离,方便维护

 

  1. redis通用配置

redis-common.conf

  1. #GENERAL  
  2. daemonize no  
  3. protected-mode no
  4. tcp-backlog 511  
  5. timeout 0  
  6. tcp-keepalive 0  
  7. loglevel notice  
  8. databases 16  
  9. dir /opt/redis/data  
  10. slave-serve-stale-data yes  
  11. #slave只读  
  12. slave-read-only yes  
  13. #not use default  
  14. repl-disable-tcp-nodelay yes  
  15. slave-priority 100  
  16. #打开aof持久化  
  17. appendonly yes  
  18. #每秒一次aof  
  19. appendfsync everysec  
  20. #关闭在aof rewrite的时候对新的写操作进行fsync  
  21. no-appendfsync-on-rewrite yes  
  22. auto-aof-rewrite-min-size 64mb  
  23. lua-time-limit 5000  
  24. #打开redis集群  
  25. cluster-enabled yes  
  26. #节点互连超时的阀值  
  27. cluster-node-timeout 15000  
  28. cluster-migration-barrier 1  
  29. slowlog-log-slower-than 10000  
  30. slowlog-max-len 128  
  31. notify-keyspace-events ""  
  32. hash-max-ziplist-entries 512  
  33. hash-max-ziplist-value 64  
  34. list-max-ziplist-entries 512  
  35. list-max-ziplist-value 64  
  36. set-max-intset-entries 512  
  37. zset-max-ziplist-entries 128  
  38. zset-max-ziplist-value 64  
  39. activerehashing yes  
  40. client-output-buffer-limit normal 0 0 0  
  41. client-output-buffer-limit slave 256mb 64mb 60  
  42. client-output-buffer-limit pubsub 32mb 8mb 60  
  43. hz 10  
  44. aof-rewrite-incremental-fsync yes  

 

  1. redis特殊配置

redis-6380.conf

  1. #包含通用配置  
  2. include /opt/redis/redis-common.conf  
  3. #监听tcp端口  
  4. port 6380  
  5. #最大可用内存  
  6. maxmemory 100m  
  7. #内存耗尽时采用的淘汰策略:  
  8. volatile-lru -> remove the key with an expire set using an LRU algorithm  
  9. # allkeys-lru -> remove any key accordingly to the LRU algorithm  
  10. volatile-random -> remove a random key with an expire set  
  11. # allkeys-random -> remove a random key, any key  
  12. volatile-ttl -> remove the key with the nearest expire time (minor TTL)  
  13. # noeviction -> don't expire at all, just return an error on write operations  
  14. maxmemory-policy allkeys-lru  
  15. #aof存储文件  
  16. appendfilename "appendonly-6380.aof"  
  17. #不开启rdb存储,只用于添加slave过程  
  18. dbfilename dump-6380.rdb  
  19. #cluster配置文件(启动自动生成)  
  20. cluster-config-file nodes-6380.conf  
  21. #部署在同一机器的redis实例,把auto-aof-rewrite搓开,因为cluster环境下内存占用基本一致.  
  22. #防止同意机器下瞬间fork所有redis进程做aof rewrite,占用大量内存  
  23. auto-aof-rewrite-percentage 80-100  

修改特殊配置对应的端口与文件名。

 

1.4.3.3 redis cluster 操作

1)初始化并构建集群

(1)启动集群相关redis服务节点

启动命令

  1. redis-server redis-6380.conf 
  2. redis-server redis-6381.conf  
  3. redis-server redis-6382.conf
  4. redis-server redis-7380.conf  
  5. redis-server redis-7381.conf 
  6. redis-server redis-7382.conf  

 

(2):使用自带的ruby工具(redis-trib.rb)构建集群 

命令 

  1. #redis-trib.rbcreate子命令构建  
  2. #--replicas 则指定了为Redis Cluster中的每个Master节点配备几个Slave节点  
  3. #节点角色由顺序决定,master之后是slave(为方便辨认,slave的端口比master1000)  
  4. redis-trib.rb create --replicas 1 10.129.41.86:6380 10.129.41.86:6381 10.129.41.86:6382 10.129.41.86:7380 10.129.41.86:7381 10.129.41.86:7382  

 

(3):检查集群状态

命令

  1. #redis-trib.rbcheck子命令构建  
  2. #ip:port可以是集群的任意节点  
  3. redis-trib.rb check 10.129.41.86:6380  

最后输出如下信息,没有任何警告或错误,表示集群启动成功并处于ok状态

  1. [OK] All nodes agree about slots configuration.  
  2. >>> Check for open slots...  
  3. >>> Check slots coverage...  
  4. [OK] All 16384 slots covered

1.4.3.4 redis cluster 客户端

Redis参数配置文件:redis.properties

 

################# RedisPoolConfig配置  #########################

#最大连接数

redis.pool.maxTotal=200

#最大空闲数

redis.pool.maxIdle=-1

#最小空闲数

redis.pool.minIdle=-1

#最大等待时间(ms

redis.pool.maxWaitMillis=100000

#使用连接时,检测连接是否成功

redis.pool.testOnBorrow=false

#返回连接时,检测连接是否成功

redis.pool.testOnReturn=true

 

######## 多台Redis服务器,采用集群模式  ##############

#cluster.count 集群个数

redis.cluster.count=6

#cluster_1

redis.cluster_1.ip=10.129.41.86

redis.cluster_1.port=7000

#cluster_2

redis.cluster_2.ip=10.129.41.86

redis.cluster_2.port=7001

#cluster_3

redis.cluster_3.ip=10.129.41.86

redis.cluster_3.port=7003

#cluster_4

redis.cluster_4.ip=10.129.41.86

redis.cluster_4.port=7003

#cluster_5

redis.cluster_5.ip=10.129.41.86

redis.cluster_5.port=7004

#cluster_6

redis.cluster_6.ip=10.129.41.86

redis.cluster_6.port=7005

 

方式一:Jedis客户端操作redis集群

 

JedisClusterUtil.java

public class JedisClusterUtil {

    private static JedisCluster jc = null;

    private static JedisClusterUtil ru = new JedisClusterUtil();

    public JedisClusterUtil() { 

        if (jc == null) { 

            Set<HostAndPort> nodes = new HashSet<HostAndPort>();

           

            String maxTotal = PropertiesUtil.getValue("redis.properties", "redis.pool.maxTotal");

            String maxIdle = PropertiesUtil.getValue("redis.properties", "redis.pool.maxIdle");

            String maxWaitMillis = PropertiesUtil.getValue("redis.properties", "redis.pool.maxWaitMillis");

            String testOnBorrow = PropertiesUtil.getValue("redis.properties", "redis.pool.testOnBorrow");

            //集群个数

            String clusterCount = PropertiesUtil.getValue("redis.properties", "redis.cluster.count");

            int count = Integer.valueOf(clusterCount);

            String cluster_ip = null;

            String cluster_port = null;

            for(int i=1;i<=count;i++){

                cluster_ip = PropertiesUtil.getValue("redis.properties", "redis.cluster_"+i+".ip");

                cluster_port = PropertiesUtil.getValue("redis.properties", "redis.cluster_"+i+".port");

                HostAndPort hostAndPort = new HostAndPort(cluster_ip,Integer.valueOf(cluster_port));

                nodes.add(hostAndPort);

            }

            JedisPoolConfig config = new JedisPoolConfig();        

            config.setMaxTotal(Integer.valueOf(maxTotal)); 

            config.setMaxIdle(Integer.valueOf(maxIdle)); 

            config.setMaxWaitMillis(Integer.valueOf(maxWaitMillis));

            boolean flag = true;

            if("true".equalsIgnoreCase(testOnBorrow)){

                flag = true;

            }else if("false".equalsIgnoreCase(testOnBorrow)){

                flag = false;

            }

            config.setTestOnBorrow(flag);

            jc = new JedisCluster(nodes,config);

        } 

    } 

   

   

    /**

     * <p>通过key获取储存在redis中的value</p>

     * <p>并释放连接</p>

     * @param key

     * @return 成功返回value 失败返回null

     */ 

    public static String get(String key){ 

        return jc.get(key); 

    } 

 

    /**

     * <p>向redis存入key和value,并释放连接资源</p>

     * <p>如果key已经存在 则覆盖</p>

     * @param key

     * @param value

     * @return 成功 返回OK 失败返回 0

     */ 

    public static String set(String key,String value){ 

        return jc.set(key, value);

}

……………

 

方式二:spring-data-redis操作redis缓存

要求Spring-data-redis版本1.7.0以上,spring版本4.0.0以上

Spring配置文件applicationContext.xml

<!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->

<cache:annotation-driven cache-manager="cacheManager" />

<bean id="propertyConfigurer"    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <property name="ignoreUnresolvablePlaceholders" value="true" />

        <property name="locations">

            <list>

                <value>classpath:redis.properties</value>

            </list>

        </property>

    </bean>

<bean id="redisNode1" class="org.springframework.data.redis.connection.RedisNode">

        <constructor-arg index="0" value="${redis.cluster_1.ip}"></constructor-arg>

        <constructor-arg index="1" value="${redis.cluster_1.port}" type="int"></constructor-arg>

    </bean>

    <bean id="redisNode2" class="org.springframework.data.redis.connection.RedisNode">

        <constructor-arg index="0" value="${redis.cluster_2.ip}"></constructor-arg>

        <constructor-arg index="1" value="${redis.cluster_2.port}" type="int"></constructor-arg>

    </bean>

    <bean id="redisNode3" class="org.springframework.data.redis.connection.RedisNode">

        <constructor-arg index="0" value="${redis.cluster_3.ip}"></constructor-arg>

        <constructor-arg index="1" value="${redis.cluster_3.port}" type="int"></constructor-arg>

    </bean>

    <bean id="redisNode4" class="org.springframework.data.redis.connection.RedisNode">

        <constructor-arg index="0" value="${redis.cluster_4.ip}"></constructor-arg>

        <constructor-arg index="1" value="${redis.cluster_4.port}" type="int"></constructor-arg>

    </bean>

    <bean id="redisNode5" class="org.springframework.data.redis.connection.RedisNode">

        <constructor-arg index="0" value="${redis.cluster_5.ip}"></constructor-arg>

        <constructor-arg index="1" value="${redis.cluster_5.port}" type="int"></constructor-arg>

    </bean>

    <bean id="redisNode6" class="org.springframework.data.redis.connection.RedisNode">

        <constructor-arg index="0" value="${redis.cluster_6.ip}"></constructor-arg>

        <constructor-arg index="1" value="${redis.cluster_6.port}" type="int"></constructor-arg>

    </bean>

   <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">

        <property name="clusterNodes">

            <set>

                <ref bean="redisNode1"/>

                <ref bean="redisNode2"/>

                <ref bean="redisNode3"/>

                <ref bean="redisNode4"/>

                <ref bean="redisNode5"/>

                <ref bean="redisNode6"/>

            </set>

        </property>

        <!-- <property name="maxRedirects" value="5"></property> -->

   </bean>

   <!-- 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="minIdle" value="${redis.pool.minIdle}" />

       <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />

       <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />

       <property name="testOnReturn" value="${redis.pool.testOnReturn}" />

   </bean>

  

   <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

        <constructor-arg ref="redisClusterConfiguration"></constructor-arg>

        <constructor-arg ref="jedisPoolConfig"></constructor-arg>

   </bean>

  

   <!-- 模板类 -->

   <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">

       <constructor-arg name="connectionFactory" ref="jedisConnectionFactory" />

   </bean>

  

   <!-- spring自己的缓存管理器,这里定义了两个缓存位置名称 ,既注解中的value -->

   <bean class="org.springframework.cache.support.SimpleCacheManager" id="cacheManager">

       <property name="caches">

           <set>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="default" />

               </bean>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="commonCache" />

               </bean>

           </set>

       </property>

</bean>

 

    1. Redis单机模式演示步骤

 

1.5.1第一步:搭建单机redis服务器

参考1.2 Redis单机模式安装步骤,并启动Redis服务。

启动命令,以10.129.41.85:6379为例,在redis安装目录执行命令:

1.5.2第二步:配置SPEED4J  Redis参数配置文件

Redis参数配置文件在Web子工程中

src/main/resources/redis.properties:Web应用启动使用

src/test/resources/redis.properties:用于Redis单元测试

由于通过单元测试类来演示单机模式Redis缓存,配置src/test/resources/redis.properties:

 

################# RedisPoolConfig配置  ##################

#最大连接数

redis.pool.maxTotal=200

#最大空闲数

redis.pool.maxIdle=-1

#最小空闲数

redis.pool.minIdle=-1

#最大等待时间(ms

redis.pool.maxWaitMillis=100000

#使用连接时,检测连接是否成功

redis.pool.testOnBorrow=false

#返回连接时,检测连接是否成功

redis.pool.testOnReturn=true

 

######## 单台Redis服务器,确保redis服务器可读可写  ##############

#主机地址(必配)

redis.host=10.129.41.85

#端口号(必配)

redis.port=6379

#超时时间,非必要

redis.timeout=100000

#密码,非必要

#redis.password=

1.5.3第三步:缓存数据演示

1.5.3.1缓存方式一 :通过工具类缓存数据

TestJedisUtil.java

public class TestJedisUtil {

   

    /**

     * 存放缓存

     */

    @Test

    public void setValue(){

        Person p = new Person();

        p.setId("001");

        p.setName("zhangsan");

        p.setAddress("jiangsu");

       

        List<Person> persons = new ArrayList<Person>();

        persons.add(p);

        System.out.println(JedisUtil.setObjToSerialization("persons", persons));

    }

    /**

     * 取出缓存

     */

    @Test

    public void getValue(){

        System.out.println(JedisUtil.getObjFromSerialization("persons", List.class));

    }

}

 

setValue方法是将一个list集合存放到缓存中,key为”persons”

存放缓存验证,查看redis-client:

 

getVlalue方法,是将刚放入到缓存中的list集合取出来

控制台输出:

 

1.5.3.2缓存方式二 :通过注解方式添加缓存数据

首先配置springRedisContextTest.xml,配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"

    xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:cache="http://www.springframework.org/schema/cache"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd

    http://cxf.apache.org/bindings/soap

    http://cxf.apache.org/schemas/configuration/soap.xsd

    http://cxf.apache.org/jaxws

    http://cxf.apache.org/schemas/jaxws.xsd">

    <!-- 单台redis服务器:实现了redisspring的整合,且实现了 通过注解操作redis缓存数据 -->

    <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->

    <cache:annotation-driven cache-manager="cacheManager" />

    <bean id="propertyConfigurer"

        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <property name="ignoreUnresolvablePlaceholders" value="true" />

        <property name="locations">

            <list>

                <value>classpath:redis.properties</value>

            </list>

        </property>

    </bean>

 

    <!--Spring工具类 -->

    <bean id="springContextUtil" class="com.spdb.speed4j.common.SpringContextUtil" />

 

    <!-- 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="minIdle" value="${redis.pool.minIdle}" />

       <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />

       <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />

       <property name="testOnReturn" value="${redis.pool.testOnReturn}" />

   </bean>

   

   <!-- 工厂实现 -->

   <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

       <constructor-arg index="0" ref="jedisPoolConfig" />

       <property name="usePool" value="false" />

       <property name="hostName" value="${redis.host}" />

       <property name="port" value="${redis.port}" />

       <property name="timeout" value="${redis.timeout}" />

       <!-- <property name="password" value="${redis.pass}"/> -->

   </bean>

  

   <!-- 模板类 -->

   <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">

       <constructor-arg name="connectionFactory" ref="jedisConnectionFactory" />

   </bean>

  

   <!-- spring自己的缓存管理器,这里定义了两个缓存位置名称 ,既注解中的value -->

   <bean class="org.springframework.cache.support.SimpleCacheManager" id="cacheManager">

       <property name="caches">

           <set>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="default" />

               </bean>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="commonCache" />

               </bean>

           </set>

       </property>

    </bean>

    <!-- redis工具类  -->

    <bean id="redisCacheUtil" class="com.spdb.speed4j.cache.redis.RedisCacheUtil">

        <property name="redisTemplate" ref="redisTemplate"/>

    </bean>

    <bean name="userService" class="com.spdb.test.redis.UserServiceImpl"></bean>

</beans>

 

新建用于测试的类

Person.java

public class Person implements Serializable{

   

    private String id;

   

    private String name;

   

    private String address;

 

    public String getId() {

        return id;

    }

    public void setId(String id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public String getAddress() {

        return address;

    }

    public void setAddress(String address) {

        this.address = address;

    }

    @Override

    public String toString() {

        return "Person [id=" + id + ", name=" + name + ", address=" + address + "]";

    }

}

 

UserServiceImpl.java

/**

 * 用于测试缓存注解的service

 * @author T-zhangkf

 *

 */

public class UserServiceImpl {

   

    @Cacheable(value = "default", key = "#id")

    public Person getPersonInfo(String id) {

        Person p = new Person();

        p.setId("003");

        p.setName("wangwu");

        p.setName("lisi");

        return p;

    }

   

    @CachePut(value = "default", key = "#id")

    public Person getPersonInfo1(String id) {

        Person p = new Person();

        p.setId("003");

        p.setName("wangwu");

        return p;

    }

 

    @CacheEvict(value = "default", key = "#id", allEntries = false, beforeInvocation = false)

    public void deletePerson(String id) {

    }

   

}

注解@Cacheable会将 “#id”(调用方法的传值)与方法的返回值,以key-value的形式存储到redis缓存中

 

 

单元测试类:TestRedisCacheAnnotation.java

/**

 * 测试:redis整合spring

 * 测试缓存注解的功能

 * @author T-zhangkf

 *

 */

public class TestRedisCacheAnnotation {

 

    ApplicationContext context = null;

    UserServiceImpl userService = null;

    @Before

    public void setup() {

        context = new ClassPathXmlApplicationContext("springRedisContextTest.xml");

        userService = (UserServiceImpl) context.getBean("userService");

    }

 

    /**

     * 测试@Cacheable注解

     * 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

     */

    @Test

    public void testCacheable() {

        Person person = (Person) userService.getPersonInfo("003");

        System.out.println(person);

    }

   

    /**

     * 测试@CachePut注解

     * @Cacheable类似,不同的是,它每次都会触发真实方法的调用

     */

    @Test

    public void testCachePut() {

        Person person = (Person) userService.getPersonInfo1("003");

        System.out.println(person);

    }

    

    /**

     * 测试@CacheEvict注解

     * @Cacheable类似,不同的是,它每次都会触发真实方法的调用

     */

    @Test

    public void testEvict(){

        userService.deletePerson("003");

    }

   

}

testCacheable方法查询用户信息并保存到Redis缓存中

@CachePut与@Cacheable功能相同,只不过@CachePut每次调用方法都会重新将该数据往缓存中添加一次。

testEvict删除数据,同时清除缓存数据 (若allEntries = true,则清空所有缓存数据)

执行该方法清除” 003”缓存:

    1. Redis哨兵模式演示步骤

1.6.1第一步:搭建哨兵模式

架构图如下:

(1)先启动主Redis服务器,以10.129.41.85:6379为主redis服务

通过info replication命令可以看出85为主redis服务

 

(2)在83上启动两个哨兵

哨兵一配置文件:sentinel_26379.conf

port 26379

daemonize yes

protected-mode no

logfile sentinel_26379.log

dir ./

sentinel monitor mymaster 10.129.41.85 6379 1

sentinel down-after-milliseconds mymaster 30000

sentinel failover-timeout mymaster 60000

sentinel parallel-syncs mymaster 1

哨兵二配置文件:sentinel_26380.conf

port 26380

daemonize yes

protected-mode no

logfile sentinel_26380.log

dir ./

sentinel monitor mymaster 10.129.41.85 6379 1

sentinel down-after-milliseconds mymaster 30000

sentinel failover-timeout mymaster 60000

sentinel parallel-syncs mymaster 1

 

将sentinel_26379.conf,sentinel_26380.conf两个哨兵配置文件放到redis安装目录

启动两个哨兵:

 

1.6.2第二步:配置SPEED4J  Redis参数配置文件

################# RedisPoolConfig配置  ###################

#最大连接数

redis.pool.maxTotal=200

#最大空闲数

redis.pool.maxIdle=-1

#最小空闲数

redis.pool.minIdle=-1

#最大等待时间(ms

redis.pool.maxWaitMillis=100000

#使用连接时,检测连接是否成功

redis.pool.testOnBorrow=false

#返回连接时,检测连接是否成功

redis.pool.testOnReturn=true

 

#### 两台台Redis服务器,采用主从模式,使用哨兵sentinel进行监听  #######

# redis

redis.master.name=mymaster

#sentinel.count 哨兵个数

redis.sentinel.count=2

#sentinel_1:哨兵1

redis.sentinel_1.ip=10.129.41.83

redis.sentinel_1.port=26379

#sentinel_2:哨兵2

redis.sentinel_2.ip=10.129.41.83

redis.sentinel_2.port=26380

 

1.6.3第三步:验证哨兵模式缓存数据

1.6.3.1缓存方式一:通过工具类缓存

单元测试类:TestJedisSentinelUtil.java

public class TestJedisSentinelUtil {

 

    @Test

    public void testSetObjToJSON(){

          System.out.println(JedisSentinelUtil.getCurrentMaster());

          /*往缓存中放对象,以json字符串的形式存放到缓存中*/

          Person p = new Person();

          p.setId("001");

          p.setName("zhangsan");

          p.setAddress("jiangsu");

          System.out.println(JedisSentinelUtil.setObjToJSON("person"+p.getId(), p));

         

          List<Person> persons = new ArrayList<Person>();

          persons.add(p);

          System.out.println(JedisSentinelUtil.setObjToJSON("persons", persons));

         

          Map<String,Person> map = new HashMap<String, Person>();

          map.put("p1", p);

          System.out.println(JedisSentinelUtil.setObjToJSON("map", map));

    }

   

    @Test

    public void testObjFromJSON(){

        /*value值为json字符串时,获取他对应的Object*/

        System.out.println(JedisSentinelUtil.getObjFromJSON("person1",Person.class  ));

        System.out.println(JedisSentinelUtil.getObjFromJSON("persons",ArrayList.class  ));

        System.out.println(JedisSentinelUtil.getObjFromJSON("map",HashMap.class  ));

    }

   

    @Test

    public void testSetObjToSerialization(){

          /*往缓存中放对象,以json字符串的形式存放到缓存中*/

          Person p = new Person();

          p.setId("001");

          p.setName("lisi");

          p.setAddress("sichuan");

          System.out.println(JedisSentinelUtil.setObjToSerialization("person"+p.getId(), p));

         

          List<Person> persons = new ArrayList<Person>();

          persons.add(p);

          System.out.println(JedisSentinelUtil.setObjToSerialization("persons", persons));

         

          Map<String,Person> map = new HashMap<String, Person>();

          map.put("p1", p);

          System.out.println(JedisSentinelUtil.setObjToSerialization("map", map));

    }

    

    @Test

    public void testObjFromSerialization(){

        /*value值为json字符串时,获取他对应的Object*/

        System.out.println(JedisSentinelUtil.getObjFromSerialization("person1",Person.class  ));

        System.out.println(JedisSentinelUtil.getObjFromSerialization("persons",ArrayList.class  ));

        System.out.println(JedisSentinelUtil.getObjFromSerialization("map",HashMap.class  ));

    }

}

testSetObjToJSON方法将对象person001,map,集合perons缓存到redis中:

testObjFromJSON方法取出person001,map,集合perons缓存

 

1.6.3.2缓存方式二:通过注解缓存

首先配置springRedisSentinelContextTest.xml,配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"

    xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:cache="http://www.springframework.org/schema/cache"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd

    http://cxf.apache.org/bindings/soap

    http://cxf.apache.org/schemas/configuration/soap.xsd

    http://cxf.apache.org/jaxws

    http://cxf.apache.org/schemas/jaxws.xsd">

    <!-- 多台redis服务器,采用主从模式,且通过sentinel对redis服务器进行监听,初步实现了redis缓存数据的容灾功能;

                          实现了redis与spring的整合,且通过注解可操作redis缓存数据 -->

    <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->

    <cache:annotation-driven cache-manager="cacheManager" />

    <bean id="propertyConfigurer"

        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <property name="ignoreUnresolvablePlaceholders" value="true" />

        <property name="locations">

            <list>

                <value>classpath:redis.properties</value>

            </list>

        </property>

    </bean>

 

    <!--Spring工具类 -->

    <bean id="springContextUtil" class="com.spdb.speed4j.common.SpringContextUtil" />

 

   <bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">

        <property name="master">

            <bean class="org.springframework.data.redis.connection.RedisNode">

                <property name="name" value="${redis.master.name}"></property>

            </bean>

        </property>

        <property name="sentinels">

            <set>

                <bean class="org.springframework.data.redis.connection.RedisNode">

                    <constructor-arg index="0" value="${redis.sentinel_1.ip}"></constructor-arg>

                    <constructor-arg index="1" value="${redis.sentinel_1.port}"></constructor-arg>

                </bean>

                <bean class="org.springframework.data.redis.connection.RedisNode">

                    <constructor-arg index="0" value="${redis.sentinel_2.ip}"></constructor-arg>

                    <constructor-arg index="1" value="${redis.sentinel_2.port}"></constructor-arg>

                </bean>

            </set>

        </property>

   </bean>

  

   <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

        <constructor-arg ref="redisSentinelConfiguration"></constructor-arg>

        <!-- <property name="hostName" value="${redis.host}" />

        <property name="port" value="${redis.port}" />

        <property name="timeout" value="${redis.timeout}"/>

        <property name="usePool" value="false" /> -->

        <!-- <property name="password" value="${redis.pass}"/> -->

   </bean>

  

   <!-- 模板类 -->

   <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">

       <constructor-arg name="connectionFactory" ref="jedisConnectionFactory" />

   </bean>

  

   <!-- spring自己的缓存管理器,这里定义了两个缓存位置名称 ,既注解中的value -->

   <bean class="org.springframework.cache.support.SimpleCacheManager" id="cacheManager">

       <property name="caches">

           <set>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="default" />

               </bean>

               <bean class="com.spdb.speed4j.cache.redis.RedisCache">

                   <property name="redisTemplate" ref="redisTemplate" />

                   <property name="name" value="commonCache" />

               </bean>

           </set>

       </property>

    </bean>

    <!-- redis工具类  -->

    <bean id="redisCacheUtil" class="com.spdb.speed4j.cache.redis.RedisCacheUtil">

        <property name="redisTemplate" ref="redisTemplate"/>

    </bean>

    <bean name="userService" class="com.spdb.speed4j.test.redis.UserServiceImpl"></bean>

</beans>

 

单元测试类:

/**

 * 测试:redis整合spring

 * 测试缓存注解的功能

 * @author T-zhangkf

 *

 */

public class TestRedisCacheAnnotation {

 

    ApplicationContext context = null;

    UserServiceImpl userService = null;

    @Before

    public void setup() {

        //context = new ClassPathXmlApplicationContext("springRedisContextTest.xml");

    context = new ClassPathXmlApplicationContext("springRedisSentinelContextTest.xml");  

 userService = (UserServiceImpl) context.getBean("userService");

    }

 

    /**

     * 测试@Cacheable注解

     * 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

     */

    @Test

    public void testCacheable() {

        Person person = (Person) userService.getPersonInfo("003");

        System.out.println(person);

    }

   

    /**

     * 测试@CachePut注解

     * @Cacheable类似,不同的是,它每次都会触发真实方法的调用

     */

    @Test

    public void testCachePut() {

        Person person = (Person) userService.getPersonInfo1("003");

        System.out.println(person);

    }

    

    /**

     * 测试@CacheEvict注解

     * @Cacheable类似,不同的是,它每次都会触发真实方法的调用

     */

    @Test

    public void testEvict(){

        userService.deletePerson("003");

    }

   

}

注解功能与单机模式中的注解功能一样

执行testCacheable方法后查询缓存数据:

执行testEvict方法后查询缓存数据:

 

1.6.4第四步:验证主从切换

以10.129.41.86:6379为从redis服务

启动10.129.41.86:6379:

通过info replication命令可以看出86为从redis服务,它的主redis为10.129.41.85:6379

查看主redis服务(10.129.41.85:6379)的info replication,见下图:

85与86主从关系已经建立:

查看85,86缓存数据:

 

从86 redis服务器已经同步85主redis服务器中的数据。

 

此时关闭85中的redis服务,或者直接宕掉85主机。

关闭85redis服务:

等待30秒,查看86 redis服务的角色:

可以看出86已经变为主redis

 

运行1.6.3.1中的测试类:TestJedisSentinelUtil.java,执行查询缓存的方法: testObjFromJSON(),

控制台输出如下:

此时的master为86,仍然可以查询出缓存数据。

 

再次启动85中的redis服务,并查看info  replication

此时85已经变为86的从机,85、86实现了主从的切换。

 

可以查看哨兵的日志文件信息,如下:

 

+swith-master mymaster 10.129.41.85 6379 10.129.41.86 6379表示主从进行了切换

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值