第一步 编写compose文件
docker-compose.yml
version: '3.8'networks:redis-network:driver: bridgeservices:redis-master:image: redis:7.2.4container_name: redis-mastercommand: ["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-networkredis-slave1:image: redis:7.2.4container_name: redis-slave1ports:- "6380:6379"depends_on:- redis-mastercommand: ["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-networkredis-slave2:image: redis:7.2.4container_name: redis-slave2ports:- "6381:6379"depends_on:- redis-mastercommand: ["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-networkredis-sentinel1:image: redis:7.2.4container_name: redis-sentinel1depends_on:- redis-master- redis-slave1- redis-slave2command: >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-networkredis-sentinel2:image: redis:7.2.4container_name: redis-sentinel2depends_on:- redis-master- redis-slave1- redis-slave2command: >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-networkredis-sentinel3:image: redis:7.2.4container_name: redis-sentinel3depends_on:- redis-master- redis-slave1- redis-slave2command: >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.5port: 6379# 哨兵模式sentinel:master: mymasternodes: 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://";/*** 单机模式*/@Beanpublic 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 {@Resourceprivate 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 {// 获取锁失败,说明有其他线程或进程正在处理数据// 可以进行重试或触发告警机制}}}