第一步 编写compose文件
docker-compose.yml
version: '3.8'
networks:
redis-network:
driver: bridge
services:
redis-master:
image: redis:7.2.4
container_name: redis-master
command: ["sh", "-c", "redis-server --protected-mode no --slave-announce-ip 192.168.0.5 --slave-announce-port 6379 --requirepass 123456"]
ports:
- "6379:6379"
networks:
- redis-network
redis-slave1:
image: redis:7.2.4
container_name: redis-slave1
ports:
- "6380:6379"
depends_on:
- redis-master
command: ["sh", "-c", "redis-server --slaveof redis-master 6379 --masterauth 123456 --requirepass 123456 --protected-mode no --slave-announce-ip 192.168.0.5 --slave-announce-port 6380"]
networks:
- redis-network
redis-slave2:
image: redis:7.2.4
container_name: redis-slave2
ports:
- "6381:6379"
depends_on:
- redis-master
command: ["sh", "-c", "redis-server --slaveof redis-master 6379 --masterauth 123456 --requirepass 123456 --protected-mode no --slave-announce-ip 192.168.0.5 --slave-announce-port 6381"]
networks:
- redis-network
redis-sentinel1:
image: redis:7.2.4
container_name: redis-sentinel1
depends_on:
- redis-master
- redis-slave1
- redis-slave2
command: >
sh -c "mkdir -p /usr/local/etc/redis &&
echo 'port 26379' > /usr/local/etc/redis/sentinel.conf &&
echo 'protected-mode no' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel auth-pass mymaster 123456' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel announce-ip 192.168.0.5' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel announce-port 26379' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel monitor mymaster 192.168.0.5 6379 2' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel down-after-milliseconds mymaster 5000' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel failover-timeout mymaster 10000' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel parallel-syncs mymaster 1' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel resolve-hostnames yes' >> /usr/local/etc/redis/sentinel.conf &&
redis-sentinel /usr/local/etc/redis/sentinel.conf"
ports:
- "26379:26379"
networks:
- redis-network
redis-sentinel2:
image: redis:7.2.4
container_name: redis-sentinel2
depends_on:
- redis-master
- redis-slave1
- redis-slave2
command: >
sh -c "mkdir -p /usr/local/etc/redis &&
echo 'port 26379' > /usr/local/etc/redis/sentinel.conf &&
echo 'protected-mode no' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel auth-pass mymaster 123456' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel announce-ip 192.168.0.5' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel announce-port 26380' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel monitor mymaster 192.168.0.5 6379 2' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel down-after-milliseconds mymaster 5000' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel failover-timeout mymaster 10000' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel parallel-syncs mymaster 1' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel resolve-hostnames yes' >> /usr/local/etc/redis/sentinel.conf &&
redis-sentinel /usr/local/etc/redis/sentinel.conf"
ports:
- "26380:26379"
networks:
- redis-network
redis-sentinel3:
image: redis:7.2.4
container_name: redis-sentinel3
depends_on:
- redis-master
- redis-slave1
- redis-slave2
command: >
sh -c "mkdir -p /usr/local/etc/redis &&
echo 'port 26379' > /usr/local/etc/redis/sentinel.conf &&
echo 'protected-mode no' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel auth-pass mymaster 123456' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel announce-ip 192.168.0.5' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel announce-port 26381' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel monitor mymaster 192.168.0.5 6379 2' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel down-after-milliseconds mymaster 5000' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel failover-timeout mymaster 10000' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel parallel-syncs mymaster 1' >> /usr/local/etc/redis/sentinel.conf &&
echo 'sentinel resolve-hostnames yes' >> /usr/local/etc/redis/sentinel.conf &&
redis-sentinel /usr/local/etc/redis/sentinel.conf"
ports:
- "26381:26379"
networks:
- redis-network
到对应目录下面执行 docker-compose up -d命令
注意事项:
1、--slave-announce-ip 设置的是主机的IP,一定要设置,不然springboot无法访问到redis服务(主从都要设置)
2、--slave-announce-port 设置的是映射到主机的端口,也需要设置(主从都要设置)
3、--requirepass redis的密码,也需要设置,不然springboot会报错redis不安全(主从都要设置)
4、--masterauth 从redis连接主redis的密码认证,必不可少(从redis都要设置)
5、sentinel announce-ip和sentinel announce-port 设置的是哨兵暴露出去的IP和端口(所有哨兵都要设置)
6、sentinel monitor mymaster 192.168.0.5 6379 2 这里得填主redis的外部ip,也就是主redis设置的slave-announce-ip参数
7、如果遇到sentinel.conf权限不够的需要设置权限 chmod 777 sentinel.conf(之前用其他方式的时候遇到过)
8、如果需要挂载文件的,需要自己根据这个yml做适当调整
第二步 验证
去主redis命令行执行
redis-cli -a 123456 info
查看从redis是否都已连接上
查看sentinel哨兵是否正常:
执行 redis-cli -p 26379
info sentinel
一切正常!(再不正常我就该疯了......踩了无数坑)
第三步 springboot集成
1、添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.25.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
2、yml配置文件
server:
port: 8084
spring:
redisson:
# 单机模式
host: 192.168.0.5
port: 6379
# 哨兵模式
sentinel:
master: mymaster
nodes: 192.168.0.5:26379,192.168.0.5:26380,192.168.0.5:26381
# 集群模式
cluster: 192.168.0.5:6379,192.168.0.5:6380,192.168.0.5:6381
# 密码
password: 123456
ip地址请换成自己的主机ip
3、config配置
package com.example.demo.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class RedissonConfig {
@Value("${spring.redisson.sentinel.nodes}")
private String sentinel;
@Value("${spring.redisson.sentinel.master}")
private String masterName;
@Value("${spring.redisson.cluster}")
private String cluster;
@Value("${spring.redisson.host}")
private String host;
@Value("${spring.redisson.port}")
private String port;
@Value("${spring.redisson.password}")
private String password;
private int timeout = 2000;
private int scanInterval = 60000;
private static String ADDRESS_PREFIX = "redis://";
/**
* 单机模式
*/
@Bean
public RedissonClient initBean() {
// 哨兵模式
if (StringUtils.isNotBlank(sentinel)) {
log.info("redis is sentinel mode");
return redissonSentinel();
}
// 集群模式
if (StringUtils.isNotBlank(cluster)) {
log.info("redis is cluster mode");
return redissonCluster();
}
// 单机模式
if (StringUtils.isNotBlank(host)) {
log.info("redis is single mode");
return redissonSingle();
}
log.error("redisson config can not support this redis mode");
return null;
}
/**
* 单机模式
*/
private RedissonClient redissonSingle() {
Config config = new Config();
String address = ADDRESS_PREFIX+host+":"+port;
//设置
config.setCodec(new StringCodec())
//这是用的集群server
.useSingleServer()
.setAddress(address)
.setTimeout(timeout)
.setPassword(password);
return Redisson.create(config);
}
/**
* 哨兵模式
*/
private RedissonClient redissonSentinel() {
String[] nodes = sentinel.split(",");
//redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
for(int i=0;i<nodes.length;i++){
nodes[i] = ADDRESS_PREFIX+nodes[i];
}
Config config = new Config();
//设置
config.setCodec(new StringCodec())
.useSentinelServers()
.setMasterName(masterName)
.setPassword(password)
.setTimeout(timeout)
.addSentinelAddress(nodes)
// 在Redisson启动期间启用sentinels列表检查,默认为true,这里我们设置为false,不检查
.setCheckSentinelsList(false);
return Redisson.create(config);
}
/**
* 集群模式
*/
private RedissonClient redissonCluster() {
String[] nodes = cluster.split(",");
//redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
for(int i=0;i<nodes.length;i++){
nodes[i] = ADDRESS_PREFIX+nodes[i];
}
Config config = new Config();
//设置
config.setCodec(new StringCodec())
//这是用的集群server
.useClusterServers()
//设置集群状态扫描时间
.setScanInterval(scanInterval)
.addNodeAddress(nodes)
.setPassword(password)
.setReadMode(ReadMode.MASTER);;
return Redisson.create(config);
}
}
4、测试
package com.example.demo.service;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class RedissonTestService {
@Resource
private RedissonClient redissonClient;
//锁过期时间
private static final Long LOCK_KEY_TIME = 120L;
public void doTest() {
//定时任务执行周期较短,为防止数据重复修改,加入锁
RLock lock = redissonClient.getLock("test");
// 尝试获取锁并设定锁的过期时间
boolean acquired = false;
try {
//获取锁
acquired = lock.tryLock(0, LOCK_KEY_TIME, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("取锁失败");
}
if (acquired) {
try {
// 执行业务逻辑
Thread.sleep(10000);
}catch (Exception e) {
log.error("处理失败");
//业务异常处理逻辑
}finally {
// 释放锁
lock.unlock();
}
} else {
// 获取锁失败,说明有其他线程或进程正在处理数据
// 可以进行重试或触发告警机制
}
}
}