Redis Sentinel安装与部署,实现redis的高可用
前言
对于生产环境,高可用是避免不了要面对的问题,无论什么环境、服务,只要用于生产,就需要满足高可用;此文针对的是redis的高可用。
接下来会有系列文章,该系列是对spring-session实现分布式集群session的共享的完整阐述,同时也引伸出缓存的实现;而此篇是该系列的第一篇。
github地址:https://github.com/youzhibing/redis
环境准备
redis版本:redis-3.0.0
linux:centos6.7
ip:192.168.11.202, 一台服务器上搭建搭建全部redis实例,包括数据节点实例以及哨兵(sentinel)实例
客户端jedis,基于spring-boot
redis主从复制
搭建一主二从的主从环境
1、redis安装
安装很简单,网上资料很多,redis官网也有说明;主要就是3步:解压,make,make install
2、数据节点配置文件
redis解压后,redis home目录下有redis配置的样例文件,我们不直接在此文件上就行修改,在redis home目录下新建文件夹master_slave,将配置文件都放于此目录下
master配置文件:redis-6379.conf
port 6379
bind 192.168.11.202
requirepass "myredis"
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"
dir "/opt/soft/redis/data" #如若master设置了认证密码,那么所有redis数据节点都配置上masterauth属性
masterauth "myredis"
slave-1配置文件:redis-6380.conf
port 6380
bind 192.168.11.202
requirepass "myredis"
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
dir "/opt/soft/redis/data" #如若master设置了认证密码,那么所有redis数据节点都配置上masterauth属性
masterauth "myredis"
slaveof 192.168.11.202 6379
slave-2配置文件:redis-6381.conf
port 6381
bind 192.168.11.202
requirepass "myredis"
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
dir "/opt/soft/redis/data" #如若master设置了认证密码,那么所有redis数据节点都配置上masterauth属性
masterauth "myredis"
slaveof 192.168.11.202 6379
3、节点启动
如下相关路径需要根据自己的情况进行改动,可能和我的不一样
[root@slave1 master_slave]# cd /opt/redis-3.0.0/master_slave/
[root@slave1 master_slave]# ./../src/redis-server redis-6379.conf
[root@slave1 master_slave]# ./../src/redis-server redis-6380.conf
[root@slave1 master_slave]# ./../src/redis-server redis-6381.conf
确认主从关系
[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6379 -a myredis info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.11.202,port=6380,state=online,offset=393,lag=0
slave1:ip=192.168.11.202,port=6381,state=online,offset=393,lag=0
master_repl_offset:393
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:392
还可以从从节点视角来看:[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6380 -a myredis info replication
如若进行顺利,按如上配置,一主二从环境搭建完毕
4、从节点作用
主要两个作用:1、作为主节点的一个备份,一旦主节点出现故障,从节点可以作为后备"顶"上来,并且保证数据尽量不丢失(主从复制是最终一致性);2、从节点可以拓展主节点的能力,一旦主节点不能支撑大并发的读操作,从节点可以在一定程度上帮助主节点分担读压力
5、主从复制问题
1、一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程需要人工干预
2、主节点的写能力受到单机的限制
3、主节点的存储能力受到单机的限制
redis sentinel部署
此片主要讲通过sentinel解决上述问题1,问题2、3则在下篇博客进行说明
1、3个sentinel节点的配置文件基本一致,区别的只是端口
sentinel-26379.conf
port 26379
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 192.168.11.202 6380 2
#redis数据master节点设置了认证,则需要如下配置
sentinel auth-pass mymaster myredis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel-26380.conf
port 26380
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 192.168.11.202 6380 2
#redis数据master节点设置了认证,则需要如下配置
sentinel auth-pass mymaster myredis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel-26381.conf
port 26381
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 192.168.11.202 6380 2
#redis数据master节点设置了认证,则需要如下配置
sentinel auth-pass mymaster myredis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
2、启动sentinel
[root@slave1 master_slave]# ./../src/redis-sentinel sentinel-26379.conf
[root@slave1 master_slave]# ./../src/redis-sentinel sentinel-26380.conf
[root@slave1 master_slave]# ./../src/redis-sentinel sentinel-26381.conf
3、sentinel确认
[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=192.168.11.202:6379,slaves=2,sentinels=3
Redis Sentinel 最终拓扑结构
4、高可用测试
手动停掉6379实例或者kill掉6379实例
[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6379 -a myredis
192.168.11.202:6379> shutdown
查看26379.log
master节点已经自动切换到192.168.11.202:6380了,重启6379,6379则是6378的从节点了。
[root@slave1 master_slave]# ./../src/redis-cli -h 192.168.11.202 -p 6380 -a myredis
192.168.11.202:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.11.202,port=6381,state=online,offset=80802,lag=1
slave1:ip=192.168.11.202,port=6379,state=online,offset=80802,lag=1
master_repl_offset:80945
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:80944
客户端(Jedis)连接
基于spring-boot开发,spring-boot-test测试, 这两者本文不做说明,网上资料很多,不熟悉的自行去补充; 工程结构如下图
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.lee</groupId>
<artifactId>redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>redis</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <!-- 日志过滤器 -->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</dependency>
</dependencies>
</project>
redis-sentinel.properties
redis.masterName=mymaster
redis.sentinels=192.168.11.202:26379,192.168.11.202:26380,192.168.11.202:26381
redis.timeout=10000
#连接master需要用到的密码,如果redis数据节点开启了连接认证
redis.password=myredis # 连接池
# 连接池最大连接数(使用负值表示没有限制)
redis.pool.maxActive=150
# 连接池中的最大空闲连接
redis.pool.maxIdle=10
# 连接池中的最小空闲连接
redis.pool.minIdle=1
# 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1
redis.pool.maxWaitMillis=3000
# 每次释放连接的最大数目
redis.pool.numTestsPerEvictionRun=50
# 释放连接的扫描间隔(毫秒)
redis.pool.timeBetweenEvictionRunsMillis=3000
# 连接最小空闲时间(毫秒)
redis.pool.minEvictableIdleTimeMillis=1800000
# 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放(毫秒)
redis.pool.softMinEvictableIdleTimeMillis=10000
# 在获取连接的时候检查有效性, 默认false
redis.pool.testOnBorrow=true
# 在空闲时检查有效性, 默认false
redis.pool.testWhileIdle=true
# 在归还给pool时,是否提前进行validate操作
redis.pool.testOnReturn=true
# 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
redis.pool.blockWhenExhausted=true
RedisConfig.java
package com.lee.redis.config; import java.util.Arrays;
import java.util.HashSet;
import java.util.Set; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource; import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool; @Configuration
@PropertySource("redis/redis-sentinel.properties")
public class RedisConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisConfig.class); @Value("${redis.masterName}")
private String masterName;
@Value("${redis.sentinels}")
private String sentinels;
@Value("${redis.timeout}")
private int timeout;
@Value("${redis.password}")
private String password; @Value("${redis.pool.maxActive}")
private int maxTotal; @Value("${redis.pool.maxIdle}")
private int maxIdle; @Value("${redis.pool.minIdle}")
private int minIdle; @Value("${redis.pool.maxWaitMillis}")
private long maxWaitMillis; @Value("${redis.pool.numTestsPerEvictionRun}")
private int numTestsPerEvictionRun; @Value("${redis.pool.timeBetweenEvictionRunsMillis}")
private long timeBetweenEvictionRunsMillis; @Value("${redis.pool.minEvictableIdleTimeMillis}")
private long minEvictableIdleTimeMillis; @Value("${redis.pool.softMinEvictableIdleTimeMillis}")
private long softMinEvictableIdleTimeMillis; @Value("${redis.pool.testOnBorrow}")
private boolean testOnBorrow; @Value("${redis.pool.testWhileIdle}")
private boolean testWhileIdle; @Value("${redis.pool.testOnReturn}")
private boolean testOnReturn; @Value("${redis.pool.blockWhenExhausted}")
private boolean blockWhenExhausted; @SuppressWarnings({ "unchecked", "rawtypes" })
@Bean
public JedisSentinelPool jedisSentinelPool(JedisPoolConfig poolConfig) {
LOGGER.info("sentinel set : {}", sentinels);
Set sentinelSet = new HashSet(Arrays.asList(sentinels.split(",")));
JedisSentinelPool jedisPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout, password);
return jedisPool;
} @Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
jedisPoolConfig
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
jedisPoolConfig
.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
jedisPoolConfig
.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
jedisPoolConfig.setTestOnBorrow(testOnBorrow);
jedisPoolConfig.setTestWhileIdle(testWhileIdle);
jedisPoolConfig.setTestOnReturn(testOnReturn);
jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted); return jedisPoolConfig;
}
}
Application.java
package com.lee.redis; import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; @Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application { public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class);
app.setBannerMode(Banner.Mode.OFF); // 是否打印banner
// app.setApplicationContextClass(); // 指定spring应用上下文启动类
app.setWebEnvironment(false);
app.run(args);
}
}
RedisTest.java
package com.lee.redis; import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool; @RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisTest { private static final Logger LOGGER = LoggerFactory.getLogger(RedisTest.class); @Autowired
private JedisSentinelPool sentinelPool; @Test
public void getNameTest() {
Jedis jedis = null;
try {
jedis = sentinelPool.getResource();
String name = jedis.get("name");
System.out.println("name is " + name);
} catch(Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
} } }
更多详情请上我的github
运行RedisTest.java的getNameTest方法(name属性已经在redis中设置,没设置的需要提前设置),得到结果:
注意点
1、有人可能会有这样的疑问:为什么通过sentinel来获取redis的连接,而不是直接连接master来获取redis连接呢?
试想一下,客户端直接通过master节点获取redis连接,如果master节点挂掉了,虽然Redis Sentinel可以完成故障转移,但是客户端无法获取这个变化,那么客户端就无法获取redis连接了;
最了解master节点信息的就是Sentinel节点集合,所以通过sentinel来获取redis连接就能满足高可用的要求了。
2、redis master的故障转移不影响客户端连接代码, 但是转移期间内,通过sentinel是获取不到主节点的连接的, 因为转移期间内master节点还没被选举出来;
参考
《Redis开发与运维》
Redis Sentinel安装与部署,实现redis的高可用的更多相关文章
- Linux下Redis的安装和部署
一.Redis介绍 Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcache类似,但很大程度补偿了Memcache的不足,它支持存储的value类型相对更多 ...
- Redis、Redis+sentinel安装(Ubuntu 14.04下Redis安装及简单测试)
Ubuntu下Redis安装两种安装方式: 1.apt-get方式 步骤: 以root权限登录,切换到/usr目录下. 接下来输入命令,apt-get install redis-server,如图: ...
- Redis学习笔记之Linux下Redis的安装和部署
0x00 Redis介绍 Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcache类似,但很大程度补偿了Memcache的不足,它支持存储的value类型相 ...
- linux下redis的安装与部署
一.Redis介绍 Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcache类似,但很大程度补偿了Memcache的不足,它支持存储的value类型相对更多 ...
- Linux下Redis的安装和部署 详细
一.Redis介绍 Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcache类似,但很大程度补偿了Memcache的不足,它支持存储的value类型相对更多 ...
- 反射实现Model修改前后的内容对比 【API调用】腾讯云短信 Windows操作系统下Redis服务安装图文详解 Redis入门学习
反射实现Model修改前后的内容对比 在开发过程中,我们会遇到这样一个问题,编辑了一个对象之后,我们想要把这个对象修改了哪些内容保存下来,以便将来查看和追责. 首先我们要创建一个User类 1 p ...
- 部署kubernetes1.8.3高可用集群
Kubernetes作为容器应用的管理平台,通过对pod的运行状态进行监控,并且根据主机或容器失效的状态将新的pod调度到其他node上,实现了应用层的高可用. 针对kubernetes集群,高可用性 ...
- [转帖]Breeze部署kubernetes1.13.2高可用集群
Breeze部署kubernetes1.13.2高可用集群 2019年07月23日 10:51:41 willblog 阅读数 673 标签: kubernetes 更多 个人分类: kubernet ...
- 使用Ansible部署etcd 3.2高可用集群
之前写过一篇手动搭建etcd 3.1集群的文章<etcd 3.1 高可用集群搭建>,最近要初始化一套新的环境,考虑用ansible自动化部署整套环境, 先从部署etcd 3.2集群开始. ...
随机推荐
- 如何完成域名和ip地址的绑定
首先,我们要知道什么是域名绑定,所谓域名绑定就是是指已选定的域名与服务器主机的空间绑定,实在是在域名注册查询上设置或者WEB服务器上设置,使一个域名被指导向一特定空间,从而使访问者访问你的域名的时候就 ...
- dedecms系统后台登陆提示用户名密码不存在
dedecms最近被曝有非常多的安全漏洞,最近有些用户反应后台管理员账号密码没有修改但无法正常登陆,提示用户名不存在,经研究发现是程序漏洞管理员被直接篡改,解决方案如下. 工具/原料 dedecms ...
- speex库音频降噪(含代码)
speex库中音频降噪效果不错,应该是应用最广泛的吧,speex库下载地址https://www.speex.org/downloads/,可以直接下载二进制代码使用,像配置OpenCV一样配置spe ...
- ::在c++中什么意思
(1)表示“域操作符” 例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时, 就要写成void A::f(),表示这个f()函数是类A的成 ...
- 用Java 实现一个表中的数据复制到另一个表中
string sql = "insert into tbl1(s1,s2,s3) select t.t1,t.t2,t.t3 from tab2 t";再用jdbc或者hibern ...
- Java数据持久层框架 MyBatis之API学习八(Java API详解)
对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...
- Python--socketserve源码分析(一)
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass 实现原理: s =socketserver.ThreadingTCPServer(参 ...
- java8大基本数据类型
基本类型 字节数 位数 最大值 最小值 byte 1byte 8bit 2^7 - 1 -2^7 short 2byte 16bit 2^15 - 1 -2^15 int 4byte 32bit 2^ ...
- junit4X系列--Rule
原文出处:http://www.blogjava.net/DLevin/archive/2012/05/12/377955.html.感谢作者的无私分享. 初次用文字的方式记录读源码的过程,不知道怎么 ...
- CRC检验
CRC(循环冗余检验码) 基本原理:在K位信息码后面加上R位校验形成N位编码(即CRC码),事先需要约定一个生成多项式G(x),校验码生成过程:将K位信息码向左移动R位然后mol(其实就是按位异或)上 ...