sentinel[ˈsentɪnl]哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。
哨兵架构下client端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点. 当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)。
sentinel也是redis实例;
整个哨兵集群都在监听reids的主机和所有从机,从而判断某个机器是否宕机等问题;
当哨兵发现主节点宕机,会自动选择某个从节点当做主节点;
如果没有哨兵架构,那么当主节点挂了的时候,需要运维人员手动将某个从节点设置为主节点,还需要修改IP等其他操作。
按照上面架构图来搭建架构:一个主机、两个从机、三个哨兵。
搭建一个主机两个从机
1.之前自己电脑上面已经存在6379、6380两个reids实例,且6380是作为从节点的。现在,我们再增加一个6381的从节点:
步骤:参考本篇----二、Redis主从架构1.redis主从架构搭建,配置从节点步骤
哨兵服务也是redis实例;
一般的策略是配置3个或者5个哨兵(奇数),此处我们配置三个哨兵;
1、复制一份sentinel.conf文件到我们统一的config目录下,原生的redis目录下就存在sentinel.conf
cp sentinel.conf config/sentinel-26379.conf
2、将相关配置修改为如下值:
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel-26379.pid" # 不需要创建该文件,文件会自动生成
logfile "26379.log" # 不需要创建该文件,文件会自动生成
dir "/opt/redis-6.2.2/data" # data目录是需要手动创建的,不过我们前面已经创建好了
# sentinel monitor <master-redis-name> <master-redis-ip> <master-redis-port> <quorum>
# quorum是一个数字:
#指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 192.168.43.10 6379 2 #mymaster这个名字随便取,客户端访问时会用到
3、自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改
复制命令: cp sentinel.conf sentinel-26380.conf
复制命令: cp sentinel.conf sentinel-26381.conf
修改的地方:port、pidfile、logfile、如果有sentinel myid这一行删掉(踩坑。知道为啥删吗?)
4、启动sentinel哨兵实例
src/redis-sentinel config/sentinel-26379.conf
src/redis-sentinel config/sentinel-26380.conf
src/redis-sentinel config/sentinel-26381.conf
5、ps -ef|grep redis查看服务是否开启
6、启动指定端口的redis服务,连接哨兵服务器。查看sentinel的info信息.
src/redis-cli -p 26379
127.0.0.1:26379>info
可以看到Sentinel的info里已经识别出了redis的主从
sentinel集群都启动完毕后,会将哨兵集群的元数据信息写入所有sentinel的配置文件里去(追加在文件的最下面),我们查看下如下配置文件sentinel-26379.conf,如下所示:
sentinel known-replica mymaster 192.168.0.60 6380 #代表redis主节点的从节点信息
sentinel known-replica mymaster 192.168.0.60 6381 #代表redis主节点的从节点信息
#代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.0.60 26380 52d0a5d70c1f90475b4fc03b6ce7c3c56935760f
#代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.0.60 26381 e9f530d3882f8043f76ebb8e1686438ba8bd5ca6
当redis主节点如果挂了,哨兵集群会重新选举出新的redis主节点,同时会修改所有sentinel节点配置文件的集群元数据信息,比如6379的redis如果挂了,假设选举出的新主节点是6380,则sentinel文件里的集群元数据信息会变成如下所示:
sentinel known-replica mymaster 192.168.0.60 6379 #代表主节点的从节点信息
sentinel known-replica mymaster 192.168.0.60 6381 #代表主节点的从节点信息
sentinel known-sentinel mymaster 192.168.0.60 26380 52d0a5d70c1f90475b4fc03b6ce7c3c56935760f #代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.0.60 26381 e9f530d3882f8043f76ebb8e1686438ba8bd5ca6 #代表感知到的其它哨兵节点
同时还会修改sentinel文件里之前配置的mymaster对应的6379端口,改为6380
sentinel monitor mymaster 192.168.0.60 6380 2
当6379的redis实例再次启动时,哨兵集群根据集群元数据信息就可以将6379端口的redis节点作为从节点加入集群.。
该代码模拟的是:Java代码的redis客户端不会直接与redis主机连接,而是通过哨兵连接redis的主机。
public class JedisSentinelTest {
public static void main(String[] args) throws IOException {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
String masterName = "mymaster";
Set<String> sentinels = new HashSet<String>();
sentinels.add(new HostAndPort("192.168.43.10",26379).toString());
sentinels.add(new HostAndPort("192.168.43.10",26380).toString());
sentinels.add(new HostAndPort("192.168.43.10",26381).toString());
//JedisSentinelPool其实本质跟JedisPool类似,都是与redis主节点建立的连接池
//JedisSentinelPool并不是说与sentinel建立的连接池,而是通过sentinel发现redis主节点并与其建立连接
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, config, 3000, null);
Jedis jedis = null;
try {
jedis = jedisSentinelPool.getResource();
System.out.println(jedis.set("sentinel", "zhuge"));
System.out.println(jedis.get("sentinel"));
} catch (Exception e) {
e.printStackTrace();
} finally {
//注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
if (jedis != null)
jedis.close();
}
}
}
springboot项目更多的采用RedisTemplate类连接redis,而不是采用Jedis。
哨兵的Spring Boot整合Redis连接代码见示例项目:redis-sentinel-cluster
1、引入相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
springboot项目核心配置:
server:
port: 8080
spring:
redis:
database: 0
timeout: 3000
sentinel: #哨兵模式
master: mymaster #主服务器所在集群名称
nodes: 192.168.43.10:26379,192.168.43.10:26380,192.168.43.10:26381
lettuce:
pool:
max-idle: 50
min-idle: 10
max-active: 100
max-wait: 1000
访问代码:
@RestController
public class IndexController {
private static final Logger logger = LoggerFactory.getLogger(IndexController.class);
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 测试节点挂了哨兵重新选举新的master节点,客户端是否能动态感知到
* 新的master选举出来后,哨兵会把消息发布出去,客户端实际上是实现了一个消息监听机制,
* 当哨兵把新master的消息发布出去,客户端会立马感知到新master的信息,从而动态切换访问的masterip
*
* @throws InterruptedException
*/
@RequestMapping("/test_sentinel")
public void testSentinel() throws InterruptedException {
int i = 1;
while (true){
try {
stringRedisTemplate.opsForValue().set("zhuge"+i, i+"");
System.out.println("设置key:"+ "zhuge" + i);
i++;
Thread.sleep(1000);
}catch (Exception e){
logger.error("错误:", e);
}
}
}
}
过了十几秒后,主从开始切换;
重新启动6379服务,启动6379客户端,执行info命令,发现现在redis主机成了6380,并且redis主节、从机、所有的哨兵都会自动修改同步主机配置数据(conf文件中):