REDIS缓存
- 缓存
-
- Redis缓存概述
Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的key-value形式存储系统,它可以用作数据库、缓存和消息中间件。Redis内置了复制(replication)、LUA脚本、LRU事件驱动、事务和不同级别的磁盘持久化,并可通过Redis哨兵(Sentinel)或者集群(Cluster)提供高可用性(HA)。
-
- 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>
关于注解添加方式,请参考工具操作手册。
-
- 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
- Port:sentinel监听端口,默认是26379
- Daemonize:默认情况下不是作为守护进程运行的,如果你想让它在后台运行,就把它改成 yes
- protected-mode:处于保护模式时只能通过本地来连接,改成no。
- logfile:sentinel日志文件
- dir:指定工作目录
- 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是不能执行自动故障迁移的。
- sentinel down-after-milliseconds mymaster 30000
表示如果30s内mymaster没响应,就认为SDOWN
- sentinel failover-timeout mymaster 60000
表示如果60秒后,mysater仍没活过来,则启动failover,从剩下的slave中选一个升级为master
- 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>
关于注解添加方式,请参考工具操作手册
-
- 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架构图
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的master节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key
2) redis-cluster选举:容错
(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)
- #download:zlib-1.2.7.tar.gz
- tar –xf zlib-1.2.7.tar.gz
- ./configure
- make
- make install
(2)安装ruby:version(1.8.7)
安装命令:
- # download:ruby-1.8.7.tar.gz
- tar –xzvf ruby-1.8.7.tar.gz
- ./configure
- make
- make install
(3)安装rubygems:version(2.6.8)
安装命令:
- # download:rubygems-2.6.8.tgz
- tar –xzvf rubygems-2.6.8.tgz
- ./configure
- Make
- make install
- cd /rubygems-2.6.8
- ruby setup.rb
- sudo cp bin/gem/usr/local/bin
(4)安装gem-redis:version(3.0.7)
安装命令:
- # download地址:http://rubygems.org/gems/redis/versions/
- # download:redis-3.0.7.gem
- gem install -l /data/soft/redis-3.0.7.gem
1.4.3.2配置redis cluster
为了方便演示,在一台机器上进行演示,创建六个redis服务实例
- redis配置文件结构
使用包含(include)把通用配置和特殊配置分离,方便维护
- redis通用配置
redis-common.conf
- #GENERAL
- daemonize no
- protected-mode no
- tcp-backlog 511
- timeout 0
- tcp-keepalive 0
- loglevel notice
- databases 16
- dir /opt/redis/data
- slave-serve-stale-data yes
- #slave只读
- slave-read-only yes
- #not use default
- repl-disable-tcp-nodelay yes
- slave-priority 100
- #打开aof持久化
- appendonly yes
- #每秒一次aof写
- appendfsync everysec
- #关闭在aof rewrite的时候对新的写操作进行fsync
- no-appendfsync-on-rewrite yes
- auto-aof-rewrite-min-size 64mb
- lua-time-limit 5000
- #打开redis集群
- cluster-enabled yes
- #节点互连超时的阀值
- cluster-node-timeout 15000
- cluster-migration-barrier 1
- slowlog-log-slower-than 10000
- slowlog-max-len 128
- notify-keyspace-events ""
- 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
- redis特殊配置
redis-6380.conf
- #包含通用配置
- include /opt/redis/redis-common.conf
- #监听tcp端口
- port 6380
- #最大可用内存
- maxmemory 100m
- #内存耗尽时采用的淘汰策略:
- # volatile-lru -> remove the key with an expire set using an LRU algorithm
- # allkeys-lru -> remove any key accordingly to the LRU algorithm
- # volatile-random -> remove a random key with an expire set
- # allkeys-random -> remove a random key, any key
- # volatile-ttl -> remove the key with the nearest expire time (minor TTL)
- # noeviction -> don't expire at all, just return an error on write operations
- maxmemory-policy allkeys-lru
- #aof存储文件
- appendfilename "appendonly-6380.aof"
- #不开启rdb存储,只用于添加slave过程
- dbfilename dump-6380.rdb
- #cluster配置文件(启动自动生成)
- cluster-config-file nodes-6380.conf
- #部署在同一机器的redis实例,把auto-aof-rewrite搓开,因为cluster环境下内存占用基本一致.
- #防止同意机器下瞬间fork所有redis进程做aof rewrite,占用大量内存
- auto-aof-rewrite-percentage 80-100
修改特殊配置对应的端口与文件名。
1.4.3.3 redis cluster 操作
1)初始化并构建集群
(1)启动集群相关redis服务节点
启动命令
- redis-server redis-6380.conf
- redis-server redis-6381.conf
- redis-server redis-6382.conf
- redis-server redis-7380.conf
- redis-server redis-7381.conf
- redis-server redis-7382.conf
(2):使用自带的ruby工具(redis-trib.rb)构建集群
命令
- #redis-trib.rb的create子命令构建
- #--replicas 则指定了为Redis Cluster中的每个Master节点配备几个Slave节点
- #节点角色由顺序决定,先master之后是slave(为方便辨认,slave的端口比master大1000)
- 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):检查集群状态
命令
- #redis-trib.rb的check子命令构建
- #ip:port可以是集群的任意节点
- redis-trib.rb check 10.129.41.86:6380
最后输出如下信息,没有任何警告或错误,表示集群启动成功并处于ok状态
- [OK] All nodes agree about slots configuration.
- >>> Check for open slots...
- >>> Check slots coverage...
- [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>
-
- 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服务器:实现了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" />
<!-- 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”缓存:
-
- 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表示主从进行了切换