首先服务器搭建哨兵模式(我这里使用的是Windows8系统),感谢两位博主,少走不少弯路,在此给出链接:服务器哨兵模式搭建和整合哨兵模式
什么一些介绍就不介绍了,可以看一下连接,比较详细,初次接触,当时很迷茫,特纠结整合时候服务器是否需要开启哨兵模式,因为在整合的时候确认了master,结果很显然是需要的
先配置服务器(本地)哨兵模式,直接从redis官网下载安装或者解压版,安装后的目录结构
然后配置哨兵模式

port:6379
#设置连接密码
requirepass:grs
#连接密码
masterauth:grs
slave.6380.conf配置
port:6380
dbfilename dump6380.rdb
#配置master
slaveof 127.0.0.1 6379
slave.6381.conf配置
port 6381
slaveof 127.0.0.1 6379
dbfilename "dump.rdb"
配置哨兵sentinel.63791.conf(其他两个哨兵配置文件一致,只修改端口号码即可)
port 63791
#主master,2个sentinel选举成功后才有效,这里的master-1是名称,在整合的时候需要一致,这里可以随便更改
sentinel monitor master-1 127.0.0.1 6379 2
#判断主master的挂机时间(毫秒),超时未返回正确信息后标记为sdown状态
sentinel down-after-milliseconds master-1 5000
#若sentinel在该配置值内未能完成failover操作(即故障时master/slave自动切换),则认为本次failover失败。
sentinel failover-timeout master-1 18000
#选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小,完成故障转移所需的时间就越长
sentinel config-epoch master-1 2
需要注意的地方
1、若通过redis-cli -h 127.0.0.1 -p 6379连接,无需改变配置文件,配置文件默认配置为bind 127.0.0.1(只允许127.0.0.1连接访问)若通过redis-cli -h 192.168.180.78 -p 6379连接,需改变配置文件,配置信息为bind 127.0.0.1 192.168.180.78(只允许127.0.0.1和192.168.180.78访问)或者将bind 127.0.0.1注释掉(允许所有远程访问)
2、masterauth为所要连接的master服务器的requirepass,如果一个redis集群中有一个master服务器,两个slave服务器,当master服务器挂掉时,sentinel哨兵会随机选择一个slave服务器充当master服务器,鉴于这种机制,解决办法是将所有的主从服务器的requirepass和masterauth都设置为一样。
3、sentinel monitor master-1 127.0.0.1 6379 2 行尾最后的一个2代表什么意思呢?我们知道,网络是不可靠的,有时候一个sentinel会因为网络堵塞而误以为一个master redis已经死掉了,当sentinel集群式,解决这个问题的方法就变得很简单,只需要多个sentinel互相沟通来确认某个master是否真的死了,这个2代表,当集群中有2个sentinel认为master死了时,才能真正认为该master已经不可用了。(sentinel集群中各个sentinel也有互相通信,通过gossip协议)。
依次启动redis
redis-server master.6379.conf
redis-server slave.6380.conf
redis-server slave.6381.conf
redis-server sentinel.63791.conf --sentinel(linux:redis-sentinel sentinel.63791.conf)其他两个哨兵也这样启动
使用客户端查看一下master状态
查看一下哨兵状态
现在就可以在master插入数据,所有的redis服务都可以获取到,slave只能读
整合spring,导入依赖
<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>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
redis.properties
#redis中心
redis.host=127.0.0.1
#redis.host=10.75.202.11
redis.port=6379
redis.password=
#redis.password=123456
redis.maxTotal=200
redis.maxIdle=100
redis.minIdle=8
redis.maxWaitMillis=100000
redis.maxActive=300
redis.testOnBorrow=true
redis.testOnReturn=true
#Idle时进行连接扫描
redis.testWhileIdle=true
#表示idle object evitor两次扫描之间要sleep的毫秒数
redis.timeBetweenEvictionRunsMillis=30000
#表示idle object evitor每次扫描的最多的对象数
redis.numTestsPerEvictionRun=10
#表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
redis.minEvictableIdleTimeMillis=60000 redis.timeout=100000
配置sentinel方式一
<!-- 这个这个bean是继承RedisSentinelConfiguration,原因是我直接使用时,在注入哨兵的时候spring获取不到注入属性的方法,老是报参数异常 -->
<bean id="redisSentinelConfiguration" class="com.uec.village.redis.RedisConfiguration"><!-- 这里是配置哨兵模式 -->
<constructor-arg name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="63791"/>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="63792"/>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="63793"/>
</bean>
</set>
</constructor-arg>
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<!--必须指定主节点名称,与服务中的master名称一致-->
<property name="name" value="master-1"/>
<!-- <constructor-arg name="host" value="${redis.host}"/> -->
<!-- <constructor-arg name="port" value="6379"/> -->
</bean>
</property>
</bean>
<!-- 可以直接配置sentinel -->
<!--
<bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="sentinels">
<set>
<bean name="sentinelNode1" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="63791"/>
</bean>
<bean name="sentinelNode2" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="63792"/>
</bean>
<bean name="sentinelNode3" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="63793"/>
</bean>
</set>
</property>
<property name="master">
<bean name="masterNode" class="org.springframework.data.redis.connection.RedisNode">
<!--必须指定主节点名称-->
<property name="name" value="mymaster"/>
<!-- <constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="6379"/>
-->
</bean>
</property>
</bean>
-->
配置sentinel方式二
<bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<constructor-arg name="propertySource" ref="propertySource"/>
</bean> <bean name="propertySource" class="org.springframework.core.io.support.ResourcePropertySource">
<constructor-arg name="location" value="classpath:spring-redis-sentinel.properties" />
</bean>
spring-redis-sentinel.properties内容:
#哨兵监控主redis节点名称,必选
spring.redis.sentinel.master=mymaster
#哨兵节点
spring.redis.sentinel.nodes=192.168.48.31:26379,192.168.48.32:26379,192.168.48.33:26379
xml配置
<!-- jedis 配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="minIdle" value="${redis.minIdle}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
<property name="testOnReturn" value="${redis.testOnReturn}" />
<property name="testWhileIdle" value="${redis.testWhileIdle}" />
<property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />
<property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />
<property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />
</bean >
<!-- redis服务器中心 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"/>
<constructor-arg name="poolConfig" ref="poolConfig" />
<!-- <property name="poolConfig" ref="poolConfig" /> 如果不是哨兵模式,把这行放开,注释掉上面两行的构造方法注入-->
<property name="port" value="${redis.port}" />
<property name="hostName" value="${redis.host}" />
<property name="password" value="${redis.password}" />
<property name="timeout" value="${redis.timeout}" ></property>
</bean >
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer" >
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean > <!-- cache配置 -->
<bean id="redisUtil" class="com.uec.village.util.RedisUtil" >
<property name="redisTemplate" ref="redisTemplate" />
</bean >
<bean id="methodCacheInterceptor" class="com.uec.village.interceptor.MethodCacheInterceptor" >
<property name="redisUtil" ref="redisUtil" />
</bean >
<!-- 配置拦截需要缓存的方法,根绝注解决定 -->
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
<property name="advice" >
<ref local="methodCacheInterceptor" />
</property>
<property name="patterns" >
<set>
<!-- 确定正则表达式列表 -->
<value>com.service.impl...*ServiceImpl.*</value >
</set>
</property>
</bean >
RedisConfiguration类
import static org.springframework.util.Assert.notNull; import java.util.Set; import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration; public class RedisConfiguration extends RedisSentinelConfiguration{ public RedisConfiguration(){}
public RedisConfiguration(Iterable<RedisNode> sentinels){
notNull(sentinels, "Cannot set sentinels to 'null'."); Set<RedisNode> sentinels2 = getSentinels();
if(!sentinels2.isEmpty()){
sentinels2.clear();
} for (RedisNode sentinel : sentinels) {
addSentinel(sentinel);
}
} }
MethodCacheInterceptor拦截器
import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import com.uec.village.annotation.RedisCache;
import com.uec.village.util.RedisUtil; /**
* 用户登录过滤器
* @author snw
*
*/
public class MethodCacheInterceptor implements MethodInterceptor {
private RedisUtil redisUtil; /**
* 初始化读取不需要加入缓存的类名和方法名称
*/
public MethodCacheInterceptor() { } @Override
public Object invoke(MethodInvocation invocation) throws Throwable { Object value = null;
String targetName = invocation.getThis().getClass().getName();
Method method = invocation.getMethod();
String methodName = method.getName();
RedisCache annotation = method.getAnnotation(RedisCache.class);
//说明当前方法不需要缓存的,
if(annotation == null){
return invocation.proceed();
}
Object[] arguments = invocation.getArguments();
String key = getCacheKey(targetName, methodName, arguments);
System.out.println(key);
try {
// 判断是否有缓存
if (redisUtil.exists(key)) {
System.out.println("方法名称为:"+methodName+",根据:"+key+",从缓存中获取");
return redisUtil.get(key);
}
// 写入缓存
value = invocation.proceed();
if (value != null) {
final String tkey = key;
final Object tvalue = value;
if(annotation.isTime()){
redisUtil.set(tkey, tvalue); }
}
return value;
} catch (Exception e) {
e.printStackTrace();
if (value == null) {
return invocation.proceed();
}
}
return value;
} /**
* 创建缓存key
*
* @param targetName
* @param methodName
* @param arguments
*/
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sbu = new StringBuffer();
sbu.append(targetName).append("_").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sbu.append("_").append(arguments[i]);
}
}
return sbu.toString();
} public void setRedisUtil(RedisUtil redisUtil) {
this.redisUtil = redisUtil;
}
}
redis工具类
import java.io.Serializable;
import java.util.Set;
import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations; /**
* redis工具
* @author snw
*
*/
public class RedisUtil { private RedisTemplate<Serializable, Object> redisTemplate; /**
* 批量删除对应的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
} /**
* 批量删除key
*
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
} /**
* 删除对应的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
} /**
* 判断缓存中是否有对应的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
} /**
* 读取缓存
*
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate
.opsForValue();
result = operations.get(key);
return result;
} /**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate
.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
} /**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate
.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
} public void setRedisTemplate(
RedisTemplate<Serializable, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
boolean isCache() default false;
}
到这里可以测试一下,你缓存的数据,是否可以直接使用客户端获取。如有不对的地方欢迎大家扔鸡蛋,以免误人子弟,谢谢!