哨兵作用:
1、通过发送命令,让Redis服务返回监测其运行状态,包括主服务和从服务器
2、当哨兵监测到主服务master宕机,会自动将slave切换成master,然后通过发布,订阅模式,通知其他从服务器,修改配置文件,让他们切换主机、
搭建流程:
1、本人采用一台电脑搭建伪集群模式,环境如下,
服务类型 | 是否主服务器 | IP地址 | 端口 |
Redis | 是 | 127.0.0.1 | 6380 |
Redis | 否 | 127.0.0.1 | 6379 |
Redis | 否 | 127.0.0.1 | 6381 |
sentinel(哨兵) | 127.0.0.1 | 26279 | |
sentinel | 127.0.0.1 | 26380 | |
sentinel | 127.0.0.1 | 26381 |
2、服务器配置
主服务器配置文件配置
requirepass 123456
port 7380
从服务器配置(不同从服务器需要设置不同端口,本人的是6379,6381)
#使得redis服务器可以跨网络访问
bind 0.0.0.0
#设置密码
requirepass "123456"
#指定主服务器
slaveof 127.0.0.1 6380
#主服务器密码
masterauth "123456"
port 6381
哨兵服务器配置
Windows下需要创建哨兵配置文件 sentinel.conf(本人把该文件放到了与 redis.windows.conf 同级目录)
#禁止保护模式
#protected-mode no
#配置监听主服务器
#sentinel monitor 代表监控
#mymaster 代表服务器名称,可以自定义
#127.0.0.1 代表主服务器ip
#6379 代表主服务器端口
#2 代表只有两个或两个以上的哨兵认为主服务器不可用时才会做故障切换操作
sentinel monitor mymaster 127.0.0.1 6380 2
#sentinel auth-pass 定义主服务器密码
#mymaster 代表服务器名称
#123456 服务器密码
sentinel auth-pass mymaster 123456
# 3s内mymaster无响应,则认为mymaster宕机了
sentinel down-after-milliseconds mymaster 3000
#指定在故障切换准许的毫秒数,当超过这个毫秒数的时候,就认为切换故障失败,默认三分钟
sentinel failover-timeout mymaster 10000
# 执行故障转移时, 最多有几个从服务器同时对新的主服务器进行同步,一般而言,这个数字越小同步时间越长,而越大,则对网络资源要求比较高
#sentinel parallel-syncs mymaster 2
#指定哨兵(sentinel)检查该redis实例指向的实例异常时,调用报警脚本,
#sentinel notification-script mymaster <脚本路径>
port 26379
**不同哨兵需要配置不同的端口
3.ssm整合redis哨兵模式
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 连接池基本参数配置-->
<!-- <context:property-placeholder location="classpath*:redis.properties" /> -->
<!-- redis数据源 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空闲数 -->
<property name="maxIdle" value="${redis.poolConfig.maxIdle}" />
<!-- 最大空连接数 -->
<property name="maxTotal" value="${redis.poolConfig.maxTotal}" />
<!-- 最大等待时间 -->
<property name="maxWaitMillis" value="${redis.poolConfig.maxWait}" />
<!-- 连接超时时是否阻塞,false时报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="${redis.poolConfig.blockWhenExhausted}" />
<!-- 返回连接时,检测连接是否成功 -->
<property name="testOnBorrow" value="${redis.poolConfig.testOnBorrow}" />
</bean>
<bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<!-- 这个值要和Sentinel中指定的master的值一致,不然启动时找不到Sentinel会报错的 -->
<property name="name" value="mymaster"/>
<!-- 配置注master节点 -->
<!--<constructor-arg name="host" value="${redis.masterHost}"/>
<constructor-arg name="port" value="${redis.masterPort}"/>-->
</bean>
</property>
<!-- sentinel哨兵监控, 这里是指定Sentinel的IP和端口,不是Master和Slave的 -->
<property name="sentinels">
<set>
<bean name="sentinelNode1" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="26379"></constructor-arg>
</bean>
<bean name="sentinelNode2" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="26380"></constructor-arg>
</bean>
<bean name="sentinelNode3" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="26381"></constructor-arg>
</bean>
</set>
</property>
</bean>
<!--redis工厂-->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!-- 单点配置 -->
<!-- <property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.password}"></property> -->
<property name="password" value="${redis.password}"></property>
<!-- 超时时间 默认2000-->
<!-- <property name="timeout" value="${redis.timeout}" /> -->
<constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"></constructor-arg>
<constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
<!-- usePool:是否使用连接池 -->
<property name="usePool" value="true"/>
</bean>
<!-- redis模板配置 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<!--以下针对各种数据进行序列化方式的选择-->
<!-- <property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property> -->
<!-- <property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
-->
</bean>
</beans>
redis相关配置文件
redis.host=127.0.0.1
redis.port=6379
redis.password=123456
redis.poolConfig.maxIdle=300
redis.poolConfig.maxTotal=600
redis.poolConfig.maxWait=100
redis.poolConfig.testOnBorrow=true
redis.poolConfig.blockWhenExhausted=true
4.启动redis 服务,启动顺序 1).启动主服务器 2)启动从服务器3)启动哨兵服务
启动命令
#启动哨兵
redis-server.exe sentinel.conf --sentinel
#启动redis
redis-server.exe redis.windows.conf
可以通过 登录redis客户端输入 info replication 命令查看redis服务器角色及部分信息
哨兵启动成功样例:
主观下线与客观下线
当某个哨兵检测到主服务宕机后,不会立马进行failover 操作,而仅仅是一个哨兵主观的认为主机已经不可用,这个现象被称为主观下线。当其他哨兵也检测到主服务不可用,并且有了一定数量的哨兵认为主服务不可用,那么哨兵之间会进行一次投票,投票结果是由一个哨兵发起,进行failover操作,在failover操作的过程中切换成功后,就会通过发布订阅的方式,让各个哨兵把自己监测的服务器实现切换主机,这个过程被称为客观下线。
当主服务宕机后,通过选举的方式选出主服务器,并且哨兵会更改redis服务器的配置文件。
5.搭建过程中可能遇到的问题(本人遇到的问题)
redis服务搭建遇到异常:
1.Invalid argument during startup: unknown conf file parameter : bind
bind 0.0.0.0 命令前有空格
2.[3056] 15 Jun 17:29:53.288 # Creating Server TCP listening socket 0.0.0.0:6379: bind: No error
端口冲突
3.
[12776] 15 Jun 17:44:37.859 #
*** FATAL CONFIG FILE ERROR ***
[12776] 15 Jun 17:44:37.859 # Reading the configuration file, at line 10
[12776] 15 Jun 17:44:37.860 # >>> 'sentinel monitor mymaster 127.0.0.1 6379 2'
[12776] 15 Jun 17:44:37.860 # sentinel directive while not in sentinel mode
问题原因:启动哨兵时命令错误
错误命令:redis-server.exe sentinel.conf
正确命令:redis-server.exe sentinel.conf --sentinel
4.[2772] 15 Jun 18:10:18.632 # Creating Server TCP listening socket *:26379: bind: No such file or directory
哨兵端口冲突,(可能是没设置端口,启动哨兵自动生成的端口与其他哨兵端口冲突,解决方案:手动设置port)
ssm整合哨兵模式遇到问题
1.Caused by: redis.clients.jedis.exceptions.JedisDataException: ERR unknown command 'SENTINEL'
问题原因:sentinel 端口配置错误
2.六月 15, 2019 11:37:37 下午 org.apache.catalina.core.StandardContext listenerStart
严重: Exception sending context initialized event to listener instance of class com.ifp.web.base.listener.SpringContextHolder
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'connectionFactory' defined in URL [file:/D:/cdy/workspace/test-workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/com.ifp.imgbusiness/WEB-INF/classes/spring_redis.xml]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:443)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:325)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5077)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5591)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1574)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1564)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at redis.clients.jedis.JedisSentinelPool.toHostAndPort(JedisSentinelPool.java:168)
at redis.clients.jedis.JedisSentinelPool.initSentinels(JedisSentinelPool.java:131)
at redis.clients.jedis.JedisSentinelPool.<init>(JedisSentinelPool.java:73)
at redis.clients.jedis.JedisSentinelPool.<init>(JedisSentinelPool.java:49)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createRedisSentinelPool(JedisConnectionFactory.java:215)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createPool(JedisConnectionFactory.java:202)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.afterPropertiesSet(JedisConnectionFactory.java:195)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 21 more
错误原因 :没有配置主服务器名称:
<bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<!-- 这个值要和Sentinel中指定的master的值一致,不然启动时找不到Sentinel会报错的 -->
<property name="name" value="${redis.masterName}"/>
</bean>
</property>
<!-- sentinel哨兵监控, 这里是指定Sentinel的IP和端口,不是Master和Slave的 -->
<property name="sentinels">
<set>
<bean name="sentinelNode1" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="26379"></constructor-arg>
</bean>
<bean name="sentinelNode2" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="26380"></constructor-arg>
</bean>
<bean name="sentinelNode3" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="26381"></constructor-arg>
</bean>
</set>
</property>
</bean>
${redis.masterName} 值为空引起异常