12、Spring技术栈-Redis Sentinel实现高可用缓存集群方案实战
来源:互联网 发布:网络mc第一红人比赛 编辑:程序博客网 时间:2024/05/21 08:04
Redis Sentinel的分布式特性介绍
Redis Sentinel是一个分布式系统,Sentinel运行在有许多Sentinel进程互相合作的环境下,它本身就是这样被设计的。有许多Sentinel进程互相合作的优点如下:
- 当多个Sentinel同意一个master不再可用的时候,就执行故障检测。这明显降低了错误概率。
- 即使并非全部的Sentinel都在工作,Sentinel也可以正常工作,这种特性,让系统非常的健康。
所有的Sentinels,Redis实例,连接到Sentinel和Redis的客户端,本身就是一个有着特殊性质的大型分布式系统。在这篇文章中,我将通过实例的形式从部署Redis Sentinel集群、使用Redis的Master Slave的模式部署Redis集群来介绍在Spring项目中如何使用Redis Sentinel实现缓存系统的高可用。
部署之前了解关于Sentinel的基本东西
- 一个健康的集群部署,至少需要三个Sentinel实例
- 三个Sentinel实例应该被放在失败独立的电脑上或虚拟机中,比如说不同的物理机或者在不同的可用区域上执行的虚拟机。
- Sentinel + Redis 分布式系统在失败期间并不确保写入请求被保存,因为Redis使用异步复制。可是有很多部署Sentinel的 方式来让窗口把丢失写入限制在特定的时刻,当然也有另外的不安全的方式来部署。
- 客户端必须支持Sentinel。大多数客户端库都支持Sentinel,但并不是全部。
- 没有高可用的设置是安全的,如果你在你的测试环境没有经常去测试,或者甚至在生产环境中你也没有经常去测试,如果Sentinel正常工作。 但是你或许有一个错误的配置而仅仅只是在很晚的时候才出现(凌晨3点你的主节点宕掉了)。
- Sentinel,Docker ,其他的网络地址转换表,端口映射应该很小心的使用:Docker执行端口重新映射,破坏Sentinel自动发现另外的Sentinel进程和一个主节点的从节点列表。在文章的稍后部分查看更过关于Sentinel和Docker的信息。
实例介绍
- 本实例通过部署3个Redis Sentinel实现Sentinel实例的高可用。
- 本实例通过部署1个Redis Master实例,3个Slave实例实现Redis缓存的高可用。
Redis Master实例和Slave实例部署
下载并安装Redis,此文不讲解,具体可参考http://blog.csdn.net/zyhlwzy/article/details/78366265。
可参考Redis主从(Master-Slave)复制(Replication)设置一文第8节Redis主从复制实战的部署方式部署Master-Slave,但是需要注意的是,本文需要的是一个Master实例,3个Slave实例。
Redis实例信息如下:
配置Master
下载并安装Redis,此文不讲解,具体可参考http://blog.csdn.net/zyhlwzy/article/details/78366265。
Master的配置很简单,我们开启守护进程即可(演示实例不设置验证信息,如有需要自行设置)。
进入redis配置文件目录(/data/redis/redis-4.0.1/redis.conf)编辑redis.conf进行如下配置。
daemonize yes
配置Slave
下载并安装Redis,此文不讲解,具体可参考http://blog.csdn.net/zyhlwzy/article/details/78366265。
创建Slave配置文件目录,拷贝redis.conf到对应目录并进行配置。
mkdir /data/redis/clustercd /data/redis/clustermkdir -p 6382cp /data/redis/redis-4.0.1/redis.conf /data/redis/cluster/6382/redis-6382.confmkdir -p 6383cp /data/redis/redis-4.0.1/redis.conf /data/redis/cluster/6383/redis-6383.confmkdir -p 6384cp /data/redis/redis-4.0.1/redis.conf /data/redis/cluster/6384/redis-6384.conf
3个Slave实例的配置文件内容,注意修改下面加粗字体部分的内容即可(注意注释掉的要去掉注释),其他都相同:
如果配置成功并启动,通过客户端工具连接到Master时,输入如下命令
info replication
将会得到如下结果
# Replicationrole:masterconnected_slaves:3slave0:ip=192.168.199.249,port=6384,state=online,offset=1127147,lag=1slave1:ip=192.168.199.249,port=6383,state=online,offset=1127292,lag=1slave2:ip=192.168.199.249,port=6382,state=online,offset=1127292,lag=1master_replid:beba8e13c44c5a4afcf8c82889b524b2f76faa22master_replid2:0000000000000000000000000000000000000000master_repl_offset:1127292second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:78717repl_backlog_histlen:1048576
Redis Sentinel部署
3个Redis Sentinel信息如下
Redis安装完成之后,在redis目录(本实例是redis-4.0.1)下会有一个sentinel.conf文件,创建sentinel目录并将该配置文件拷贝进去重命名为自定义的sentinel配置文件。
mkdir /data/redis/sentinelcp /data/redis/redis-4.0.1/sentinel.conf /data/redis/sentinel/sentinel_26379.confcp /data/redis/redis-4.0.1/sentinel.conf /data/redis/sentinel/sentinel_26479.confcp /data/redis/redis-4.0.1/sentinel.conf /data/redis/sentinel/sentinel_26579.conf
sentinel_26379.conf配置内容:
protected-mode noport 26379sentinel monitor mymaster 192.168.199.249 6384 2sentinel failover-timeout mymaster 60000sentinel config-epoch mymaster 1sentinel leader-epoch mymaster 1sentinel known-slave mymaster 192.168.199.126 6379
sentinel_26479.conf配置内容:
protected-mode noport 26479sentinel monitor mymaster 192.168.199.249 6384 2sentinel failover-timeout mymaster 60000sentinel config-epoch mymaster 1sentinel leader-epoch mymaster 1sentinel known-slave mymaster 192.168.199.126 6379
sentinel_26579.conf配置内容:
protected-mode noport 26579sentinel monitor mymaster 192.168.199.249 6384 2sentinel failover-timeout mymaster 60000sentinel config-epoch mymaster 1sentinel leader-epoch mymaster 1sentinel known-slave mymaster 192.168.199.126 6379
启动Redis Master实例
进入Redis Master服务器Redis bin目录,执行以下命令:
./redis-server /data/redis/redis-4.0.1/redis.conf
启动Redis Slave实例
进入Redis Slave服务器Redis bin目录,执行以下命令:
./redis-server /data/redis/cluster/6382/redis-6382.conf./redis-server /data/redis/cluster/6383/redis-6383.conf./redis-server /data/redis/cluster/6384/redis-6384.conf
启动Sentinel实例
进入Redis Slave服务器Redis bin目录,执行以下命令:
./redis-sentinel /data/redis/sentinel/sentinel_26379.conf ./redis-sentinel /data/redis/sentinel/sentinel_26479.conf ./redis-sentinel /data/redis/sentinel/sentinel_26579.conf
如果启动时出现如下信息,表示启动成功
Maven配置
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.7.5.RELEASE</version></dependency>
在config.properties中增加如下配置
#Redis sentinel使用redis.sentinel1.host=192.168.199.126redis.sentinel1.port=26379redis.sentinel2.host=192.168.199.126redis.sentinel2.port=26479redis.sentinel3.host=192.168.199.126redis.sentinel3.port=26579redis.sentinel.masterName=mymaster
新建spring-redis-sentinel.xml配置文件,写入如下信息
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:c='http://www.springframework.org/schema/c' xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd" default-lazy-init="true"> <!-- 开启spring cache注解功能 --> <cache:annotation-driven cache-manager="redisCacheManager" /> <context:annotation-config /> <context:property-placeholder ignore-unresolvable="true" location="classpath:config.properties" /> <!-- Redis --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="${redis.maxTotal}" /> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration"> <property name="master"> <bean class="org.springframework.data.redis.connection.RedisNode"> <property name="name" value="${redis.sentinel.masterName}"></property> </bean> </property> <property name="sentinels"> <set> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${redis.sentinel1.host}"></constructor-arg> <constructor-arg name="port" value="${redis.sentinel1.port}"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${redis.sentinel2.host}"></constructor-arg> <constructor-arg name="port" value="${redis.sentinel2.port}"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${redis.sentinel3.host}"></constructor-arg> <constructor-arg name="port" value="${redis.sentinel3.port}"></constructor-arg> </bean> </set> </property> </bean> <!-- redis服务器中心 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg> <constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> </bean> <!-- redis缓存管理器 --> <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> <constructor-arg name="redisOperations" ref="redisTemplate" /> </bean> <bean id="redisUtils" class="ron.blog.blog_service.utils.RedisUtils" /></beans>
在spring-context.xml引入spring-redis-sentinel.xml
<import resource="classpath:spring-redis-sentinel.xml" />
编写Redis帮助类RedisUtils
public class RedisUtils { /** * RedisTemplate是一个简化Redis数据访问的一个帮助类, * 此类对Redis命令进行高级封装,通过此类可以调用ValueOperations和ListOperations等等方法。 */ @Autowired private RedisTemplate<Serializable, Object> redisTemplate; /** * 批量删除对应的value * * @param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 批量删除key * * @param pattern */ public void removePattern(final String pattern) { Set<Serializable> keys = redisTemplate.keys(pattern); if (keys.size() > 0) redisTemplate.delete(keys); } /** * 删除对应的value * @param key */ public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * 缓存是否存在 * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 读取缓存 * @param key * @return */ public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result; } /** * * @Author Ron * @param key * @param hashKey * @return */ public Object get(final String key, final String hashKey){ Object result = null; HashOperations<Serializable,Object,Object> operations = redisTemplate.opsForHash(); result = operations.get(key, hashKey); return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * * @Author Ron * @param key * @param hashKey * @param value * @return */ public boolean set(final String key, final String hashKey, Object value) { boolean result = false; try { HashOperations<Serializable,Object,Object> operations = redisTemplate.opsForHash(); operations.put(key, hashKey, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } }
以上的所有配置,如果不出问题,那么基本上我们已经集成了Spring与Redis Sentinel,接下来就是在我们的例子中使用。
在我们的Blog服务(BlogService)中,我们自动装配RedisUtils实例,在进入博客详细页面时,我们首先到Redis中获取是否存在我们所要查看的博客,如果没有,则从MySQL获取并写入Redis。
@Autowired private BlogContentMapper blogContentMapper;//读者自行定义 @Autowired RedisUtils redisUtils; /** * @Comment 获取博客内容 * @Author Ron * @Date 2017年10月25日 下午3:05:27 * @return */ @Override public BlogContent getBlog(String bid) { if(redisUtils.exists(bid)){ logger.info("缓存命中博客"+bid); return (BlogContent) redisUtils.get(bid); }else{ logger.info("缓存尚未命中博客"+bid); BlogContent blogContent = blogContentMapper.selectByPrimaryKey(bid); redisUtils.set(bid, blogContent); return blogContent; } }
实例演示
我们第一次进入指定博客页面时会在控制台打出缓存尚未命中博客的信息,第二次进入则会打出缓存命中博客字样。
博客详细页面:
第一次控制台打出的信息:
第二次刷新页面是空值台打出的信息:
先查看一下Sentinel信息:
输入命令:./redis-cli -p 26379 info Sentinel
# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0:name=mymaster,status=ok,address=192.168.199.126:6379,slaves=3,sentinels=4
我们可以看到Master的名称、ip地址、Slave的数量、Sentinel数量。
我们停掉一个Sentinel,在我们的控制台中就会监控到并出现连接丢失的错误日志,但是我们的系统仍然可用。
重新启动之后报错日志就停止。
我们停掉Master,在Sentinel中就会重新选举一个Slave为Master,同时在我们的Eclipse的控制台也可以看到重新选举的Master;
我们看到系统重新选举192.168.199.126 6382为Master。
注意:
启动服务时,可能会报连接Redis Sentinel超时,此时可能是防火墙未关闭造成,使用如下命令关闭防火墙即可:
在CentOS 7中默认使用firewall做为防火墙,下面是启动&关闭防火墙的命令:
// 启动firewallsystemctl start firewalld.service// 关闭firewallsystemctl stop firewalld.service
- 12、Spring技术栈-Redis Sentinel实现高可用缓存集群方案实战
- CentOS 7.3 Sentinel实现Redis集群高可用部署
- 基于Sentinel(哨兵)搭建实现Redis高可用集群
- redis高可用方案Sentinel配置
- Redis Sentinel主从高可用方案
- Redis sentinel 高可用主从配置方案
- Redis 高可用集群管理工具Sentinel
- Redis Sentinel高可用集群Java客户端
- Redis高可用集群Sentinel哨兵
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案【收藏】
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案
- useBodyEncodingForURI="true"解决Tomcat乱码问题
- oracle 查询并清除锁lock
- 循环中标签中使用 break outer continue outer
- Android 5.1设置默认输入法
- android 自定义圆形图片
- 12、Spring技术栈-Redis Sentinel实现高可用缓存集群方案实战
- mysql常用命令
- 《颠覆者》感悟
- dp与px的转换对照表
- JAVA设计模式之迭代器模式
- IEnumerable、GetEnumerator、IEnumerator之间的区别
- 系统间通信方式之(ActiveMQ的使用性能优化之干柴烈火4)(十五)
- CHAPTER 14 -Recurrent Neural Networks
- undefined reference to `__aeabi_uidivmod'和undefined reference to `__aeabi_uidiv'错误