前几天把redis又重新梳理了一次。主要还是对redis的查缺补漏以及更深入的了解。
这里算是一篇总结笔记叭,先从linux服务器redis主从复制,哨兵集群搭建开始。到后边springboot集成redis。
我们一步一步来。
一:redis搭建主从复制集群。
1:只需要配置从库,不需要配置主库
makefile
127.0.0.1:6379> info replication # 查看当前库信息
# Replication
role:master # 角色
connected_slaves:0 # 没有从机
master_failover_state:no-failover
master_replid:3cbed6e6f0629848071e0929f6d3f561d7498360
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:54
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
2:配置redis.conf
我没有多台服务器,因此我在一台服务器上边搭建redis集群,一主二从。三个redis。
首先我们来准备三个redis.conf配置文件:redis79.conf/redis80.conf/redis81.conf
我这里根据端口号命名。修改redis79.conf
bash
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dbfilename dump6379.rdb
#如果你是用的是aof备份机制,还需要修改:
appendfilename "appendonly6379.aof"
3:配置从机
Redis默认是主机,因此,不需要配置主机,只需要配置从机即可。
配置从机很简单,让他知道那个是主机就可以了,我这里将79认定为主机,那么80/81两个就是从机。这个配置需要到80/81上边去操作:
配置主从,一般都在配置文件中配置,这样才是永久的。
修改从机中的redis.conf:
xml
replicaof <masterip> <masterport> # 配置主机
masterauth <master-password> # 配置主机密码
依次修改上方的配置文件。
修改成功之后,依次使用配置文件启动redis-server
bash
redis-server rconfig/redis79.conf
redis-server rconfig/redis80.conf
redis-server rconfig/redis81.conf
从机配置完成之后,我们再去主机下边看一下主机的信息:
makefile
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2 # 两个从机
slave0:ip=127.0.0.1,port=6380,state=online,offset=210,lag=0 # 从机1信息
slave1:ip=127.0.0.1,port=6381,state=online,offset=210,lag=1 # 从机2信息
master_failover_state:no-failover
master_replid:29d7972ef844579fb6a727bbbce017e58e35c45e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:224
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:224
这里注意,每次修改完配置之后,重启redis需要将持久化文件(.rdb/.aof)文件删除,否则可能会有问题。
二:配置哨兵集群
创建哨兵模式配置文件sentinel.conf:
yaml
# 绑定ip
bind 0.0.0.0
# 监听端口
port 26379
# 哨兵监听的ip和端口,这里监听ip要和springboot中配置的redis的ip一致
sentinel announce-ip "127.0.0.1"
# 后面的2 表示有2个哨兵认为主库挂了就是客观下线
# 就会开始选举
# 5000 表示每5秒钟检测一次主库是否挂掉,这里监听ip要和springboot中配置的redis的ip一致
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel failover-timeout mymaster 5000
# linux中 解除redis保护 允许外部连接
protected-mode no
# 后台访问
daemonize yes
更多sentienl配置请移步《Redis重制(十九)哨兵模式》
这里需要注意一下:
yaml
sentinel monitor mymaster 127.0.0.1 6379 1
中的mymaster,这个是redis主节点的名称,叫什么都行,这个要在后边的springboot配置中使用。
哨兵要保证高可用,也要配置集群,需要多个配置文件,监听不同端口,启动多个哨兵。
bash
redis-sentinel rconfig/sentinel79.conf
redis-sentinel rconfig/sentinel80.conf
redis-sentinel rconfig/sentinel81.conf
这里需要注意,先启动redis集群,再启动哨兵集群。
三:springboot集成redis
1:配置application.yml配置文件
yaml
spring:
#redis
redis:
# 超时时间
timeout: 10000
# 使用的数据库索引,默认是0
database: 0
# 密码
# password: 123456
###################以下为肌ed1s哨兵增加的配置###########################
sentinel:
master: mymaster # 哨兵主节点名称,自定义这就是上边配置主节点的时候的名字
nodes: 1.1.1.1:26379, 1.1.1.1:26380, 1.1.1.1:26381
##################以下为]ettuce连接池增加的配置###########################
lettuce:
pool:
max-active: 100 #连接池最大连接数(使用负值表示没有限制)
max-idle: 100 #连接池中的最大空闲连接
min-idle: 50 #连接池中的最小空闲连接
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
注意上边配置中的spring.redis.sentinel.master:mymaster
这其中的mymaster就是哨兵模式中,主节点的名称。
2:添加RedisConfig.java配置
typescript
package com.modules.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.support.collections.RedisProperties;
@Configuration
@EnableCaching //开启注解
@ConditionalOnClass() // 替换springboot原有配置
public class RedisConfig extends CachingConfigurerSupport {
/**
* retemplate相关配置(这是一个固定模板,工作中可以直接用)
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
//String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(stringRedisSerializer);
// 值采用json序列化
template.setValueSerializer(jacksonSeial);
// 设置hash key 和value序列化模式
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
return template;
}
/**
* redis哨兵模式配置类修改
* 这部分配不配应该都可以
*/
/*@Bean
public RedisSentinelConfiguration redisSentinelConfiguration()
{
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("1.15.157.156",26379)
.sentinel("1.15.157.156",26380)
.sentinel("1.15.157.156",26381);
return redisSentinelConfiguration;
}//*/
/**
* 对hash类型的数据操作
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* 对redis字符串类型数据操作
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}
/**
* 对链表类型的数据操作
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
/**
* 对无序集合类型的数据操作
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
/**
* 对有序集合类型的数据操作
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
/**
* 对GEO类型的数据操作
*/
@Bean
public GeoOperations<String, Object> geoOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForGeo();
}
/**
* 对hyperloglog类型的数据操作
*/
@Bean
public HyperLogLogOperations<String, Object> hyperLogLogOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHyperLogLog();
}
/**
* 对bitmap类型的数据操作
*/
// @Bean
// public BitMapOperations<String, Object> opsForBitmap(RedisTemplate<String, Object> redisTemplate) {
// return redisTemplate.opsForBitmap();
// }
}
重要部分都有注释,对照即可。这个配置类是开箱即用的。
3:配置utils工具类
普通redis操作类:RedisUtil.java
kotlin
package com.modules.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public RedisUtil(RedisTemplate<String, Object> redisTemplate)
{
this.redisTemplate = redisTemplate;
}
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key,long time)
{
try
{
if(time>0)
{
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key,TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key)
{
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String ... key)
{
if(key!=null&&key.length>0)
{
if(key.length==1)
{
redisTemplate.delete(key[0]);
}
else
{
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key==null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key,Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key,Object value,long time){
try {
if(time>0){
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}else{
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta){
if(delta<0){
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta){
if(delta<0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object,Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* 使用模糊查询找到匹配的键
* @param pattern
* @return
*/
public List<String> findKeysByPattern(String pattern) {
// 使用模糊查询找到匹配的键
Set<String> keys = redisTemplate.keys(pattern);
List<String> list = new ArrayList<>(keys);
// 返回所有匹配的键
return list;
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String,Object> map){
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String,Object> map, long time){
try {
redisTemplate.opsForHash().putAll(key, map);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value,long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item){
redisTemplate.opsForHash().delete(key,item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item){
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item,double by)
{
return redisTemplate.opsForHash().increment(key, item,-by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key)
{
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key,Object value){
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object...values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key,long time,Object...values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if(time>0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object ...values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end){
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key){
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key,long index){
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index,Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key,long count,Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
redis加锁工具类:RedisLuaUtils.java
typescript
package com.modules.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import java.nio.charset.StandardCharsets;
/**
* @author camellia
* redis 加锁工具类
*/
@Slf4j
public class RedisLuaUtils
{
/**
* 超时时间(毫秒)
*/
private static final long TIMEOUT_MILLIS = 15000;
/**
* 重试次数
*/
private static final int RETRY_TIMES = 10;
/***
* 睡眠时间(重试间隔)
*/
private static final long SLEEP_MILLIS = 500;
/**
* 用来加锁的lua脚本
* 因为新版的redis加锁操作已经为原子性操作
* 所以放弃使用lua脚本
*/
private static final String LOCK_LUA =
"if redis.call("setnx",KEYS[1],ARGV[1]) == 1 " +
"then " +
" return redis.call('expire',KEYS[1],ARGV[2]) " +
"else " +
" return 0 " +
"end";
/**
* 用来释放分布式锁的lua脚本
* 如果redis.get(KEYS[1]) == ARGV[1],则redis delete KEYS[1]
* 否则返回0
* KEYS[1] , ARGV[1] 是参数,我们只调用的时候 传递这两个参数就可以了
* KEYS[1] 主要用來传递在redis 中用作key值的参数
* ARGV[1] 主要用来传递在redis中用做 value值的参数
*/
private static final String UNLOCK_LUA =
"if redis.call("get",KEYS[1]) == ARGV[1] "
+ "then "
+ " return redis.call("del",KEYS[1]) "
+ "else "
+ " return 0 "
+ "end ";
/**
* 检查 redisKey 是否上锁(没加锁返回加锁)
*
* @param redisKey redisKey
* @param template template
* @return Boolean
*/
public static Boolean isLock(String redisKey, String value, RedisTemplate<Object, Object> template)
{
return lock(redisKey, value, template, RETRY_TIMES);
}
private static Boolean lock(String redisKey, String value, RedisTemplate<Object, Object> template, int retryTimes)
{
boolean result = lockKey(redisKey, value, template);
while (!(result) && retryTimes-- > 0)
{
try
{
log.debug("lock failed, retrying...{}", retryTimes);
Thread.sleep(RedisLuaUtils.SLEEP_MILLIS);
}
catch (InterruptedException e)
{
return false;
}
result = lockKey(redisKey, value, template);
}
return result;
}
private static Boolean lockKey(final String key, final String value, RedisTemplate<Object, Object> template)
{
try
{
RedisCallback<Boolean> callback = (connection) -> connection.set(
key.getBytes(StandardCharsets.UTF_8),
value.getBytes(StandardCharsets.UTF_8),
Expiration.milliseconds(RedisLuaUtils.TIMEOUT_MILLIS),
RedisStringCommands.SetOption.SET_IF_ABSENT
);
return template.execute(callback);
}
catch (Exception e)
{
log.info("lock key fail because of ", e);
}
return false;
}
/**
* 释放分布式锁资源
*
* @param redisKey key
* @param value value
* @param template redis
* @return Boolean
*/
public static Boolean releaseLock(String redisKey, String value, RedisTemplate<Object, Object> template)
{
try
{
RedisCallback<Boolean> callback = (connection) -> connection.eval(
UNLOCK_LUA.getBytes(),
ReturnType.BOOLEAN,
1,
redisKey.getBytes(StandardCharsets.UTF_8),
value.getBytes(StandardCharsets.UTF_8)
);
return template.execute(callback);
}
catch (Exception e)
{
log.info("release lock fail because of ", e);
}
return false;
}
}
/**
@Resource
private RedisTemplate<Object, Object> redisTemplate;
@PostMapping("/order")
public String createOrder() throws InterruptedException {
log.info("开始创建订单");
Boolean isLock = RedisDistributedLock.isLock("testLock", "456789", redisTemplate);
if (!isLock) {
log.info("锁已经被占用");
return "fail";
} else {
//.....处理逻辑
}
Thread.sleep(10000);
//一定要记得释放锁,否则会出现问题
RedisDistributedLock.releaseLock("testLock", "456789", redisTemplate);
return "success";
}
*/
//参考文章:
//https://blog.csdn.net/Z99263/article/details/140448173
//https://www.jb51.net/article/279367.htm
//https://blog.51cto.com/u_14844/8822845
代码配置完成。
下面我们来启动项目,启动项目没有问题,在我刷新页面的时候,报错:
vbnet
org.springframework.data.redis.RedisSystemException: Error in execution;
nested exception is io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica.
报这个错,就说明我们的spring.redis.sentinel.master:mymaster这个配置项有问题。
1:首先检查代码中的配置项是否都是mymaster
2:其次再检查redis中的哨兵主节点名称是否是:mymaster
使用
bash
redis-cli -p 26379 # 链接哨兵
执行:
bash
info sentinel # 查看sentinel信息
makefile
[root@VM-4-16-centos bin]# redis-cli -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=0,sentinels=3
如果redis中和代码中的名称都是mymaster。重启项目应该就没有问题了。
四:测试一下:
1:编写一个方法,测试一下:
csharp
@Autowired
private RedisUtil redisUtil;
@GetMapping("index/redis")
public void testRedis() throws InterruptedException
{
System.out.println("开始存储redis");
redisUtil.set("kk1","123");
// 线程睡一会儿
Thread.sleep(100);
// 获取redis存储值
String kk1 = (String)redisUtil.get("kk1");
// 打印
System.out.println("kk1:"+kk1);
System.out.println("redis获取结束");
}
控制台输出:
makefile
开始存储redis
kk1:123
redis获取结束
2:进入服务器的redis-cli,查看kk1的值
csharp
[root@VM-4-16-centos bin]# redis-cli -p 6379
127.0.0.1:6379> get kk1
""123""
127.0.0.1:6379>
[root@VM-4-16-centos bin]# redis-cli -p 6380
127.0.0.1:6380> get kk1
""123""
127.0.0.1:6380>
至此,redis搭建哨兵模式集群,以及springboot集成redis哨兵模式到此完成。
有好的建议,请在下方输入你的评论。