标签归档:NoSQL

Redis Sentinel冗余配置为Redis实例提供自动故障切换

关于 Redis Sentinel

Redis主从复制能够生成一个或多个Redis服务器的副本,但是它不会在主服务器和从Redis服务器之间提供自动故障切换。因此Sentinel为Redis实例提供了一个简单而自动化的高可用性(HA)解决方案,如果当前的主服务器不按预期工作,则可以将从服务器升级为主服务器。假设您已经有一个Redis Replication集群,你将需要配置Sentinel(哨兵),从而完成故障自动切换。更多介绍请参阅官方Redis Sentinel 文档。

Redis源码包中已经包含了一个 sentinel.conf 作为Sentinel的配置文件,配置文件中自带了关于各个配置项的解释。先上架构图:

+———————–+                        +——————–+
| Redis Master:6379  |     _____       | Redis Svale:6379|
| Sentinel1:26379       |                         | Sentinel2:26379 |
+———————–+                        +——————–+

所有Redis节点都应以相同的方式配置和类似的服务器规格,如在故障转移情况下,任何从站都可能会由Sentinel升级为新的Master。

1.开始下面部署前,建议先预读以下文章:

Linux Centos7 Redis 4.0.1 源码编译安装配置
如何在 Centos 7上配置 Redis Replication 主从复制群集

2.现在开始部署(由于资源有限,暂用2台Server做为测试):

OS:CentOS 7.4
Redis sentinel 4.0.1
Redis Master+Sentinel 10.10.204.64
Redis Slave+Sentinel 10.10.204.65

3.分别修改主从的哨兵配置文件 sentinel.conf (除bind不一样外,其他均相同):

# vim /usr/local/redis/sentinel.conf

 bind 10.10.204.64 #网卡绑定的IP地址
 sentinel monitor mymaster 10.10.204.64 6379 2 #填写Master的IP地址以及端口,这个2代表;当集群中有2个sentinel认为master挂了时,才能真正认为该master已经不可用了
 sentinel down-after-milliseconds mymaster 5000 #如果5秒内检测不到mymaster节点存活,则认为主节点故障从而进行转移操作
 sentinel failover-timeout mymaster 180000 #故障转移的超时时间(单位毫秒)
 sentinel parallel-syncs mymaster 1 #设置故障转移后,允许多少从服务器连接主节点发起同步请求
 sentinel auth-pass mymaster RenwoleQxl5qpKHrh9khuTW #设置连接密码
 protected-mode no #为了redis client能内网连接操作redis-sentinel
 logfile /usr/local/redis/logs/sentinel.log #添加指定日志文件存储位置

4.分别在主从上创建Redis sentinel系统单元文件:

# vim /usr/lib/systemd/system/redis-sentinel.service

[Unit]
 Description=Redis persistent key-value database
 After=network.target

[Service]
 User=redis
 Group=redis
 ExecStart=/usr/local/bin/redis-sentinel /usr/local/redis/sentinel.conf --daemonize no
 ExecStop=/usr/local/bin/redis-cli -p 26379 shutdown
 Restart=always

[Install]
 WantedBy=multi-user.target

5.重载systemctl并启动sentinel(哨兵机制)服务:

 # systemctl daemon-reload
 # systemctl start redis-sentinel.service
 # systemctl enable redis-sentinel.service

6.将端口加入防火墙(要保证所有Redis实例相互通信):

 # firewall-cmd --zone=public --add-port=26379/tcp --permanent
 # firewall-cmd --reload

7.验证Redis故障切换:

查看Master 10.10.204.64角色,以及slave0的连接状态(正常):

 10.10.204.64:6379> info
 ...
 # Replication
 role:master
 connected_slaves:1
 slave0:ip=10.10.204.65,port=6379,state=online,offset=8681829,lag=1
 master_replid:0ed3591a6caf4ae4b59d3943dc8d7f4c0440b724
 master_replid2:0000000000000000000000000000000000000000
 master_repl_offset:8681829
 ...

查看Slave 10.10.204.65角色,以及master连接状态(正常):

 10.10.204.65:6379> info
 ...
 # Replication
 role:slave
 master_host:10.10.204.64
 master_port:6379
 master_link_status:up
 master_last_io_seconds_ago:1
 master_sync_in_progress:0
 slave_repl_offset:8692657
 ...

8.停止Redis Master服务器并查看sentinel日志记录:

 # systemctl stop redis
 # cat /usr/local/redis/logs/sentinel.log
 5403:X 11 Aug 11:05:47.633 * +slave slave 10.10.204.64:6379 10.10.204.64 6379 @ mymaster 10.10.204.65 6379
 5403:X 11 Aug 11:05:52.694 # +sdown slave 10.10.204.64:6379 10.10.204.64 6379 @ mymaster 10.10.204.65 6379

9.再查看打印的 Redis Slave sentinel日志记录:

 # cat /usr/local/redis/logs/sentinel.log
 2873:X 11 Aug 11:05:25.006 * +slave slave 10.10.204.64:6379 10.10.204.64 6379 @ mymaster 10.10.204.65 6379
 2873:X 11 Aug 11:05:30.061 # +sdown slave 10.10.204.64:6379 10.10.204.64 6379 @ mymaster 10.10.204.65 6379

日志中分别表示,已经将之前 Redis Slave 10.10.204.65 变成了主。

10.现在再模拟下之前的Redis Master 10.10.204.64上线后的状态:

 # systemctl start redis
 # cat /usr/local/redis/logs/sentinel.log
 5403:X 11 Aug 11:15:38.743 # -sdown slave 10.10.204.64:6379 10.10.204.64 6379 @ mymaster 10.10.204.65 6379
 5403:X 11 Aug 11:15:48.691 * +convert-to-slave slave 10.10.204.64:6379 10.10.204.64 6379 @ mymaster 10.10.204.65 6379

日志明确显示 Redis Master 10.10.204.64 被降级为 Redis Slave 10.10.204.65 的从,再不会变成Master,除非Slave出现故障。

扩展阅读:

查看Sentinel状态:

 # redis-cli -p 26379 -h 10.10.204.64 -a Qxl5qpKHrh9khuTW
 10.10.204.64:26379> info sentinel
 # Sentinel
 sentinel_masters:1
 sentinel_tilt:0
 sentinel_running_scripts:0
 sentinel_scripts_queue_length:0
 sentinel_simulate_failure_flags:0
 master0:name=mymaster,status=ok,address=10.10.204.65:6379,slaves=1,sentinels=2

常用命令:

sentinel master mymaster #查看Master的状态信息
SENTINEL slaves mymaster #查看Salves的信息
SENTINEL sentinels mymaster #查看哨兵的状态
SENTINEL get-master-addr-by-name mymaster #获取当前master的地址

一旦一个Sentinel成功对一个Master进行了failover,它将会把关于Master的最新配置通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。

注:如果不能正常故障切换,请检查您的机器之间的端口是否通信,大多数都是因为这个原因导致。

到目前为止Redis Sentinel已经配置完成,而且测试数据看起来一切都很好。

如何在CentOS 7上配置 Redis Replication 主从复制群集

关于 Redis Replication

Redis支持异步主从复制,允许一个或多个Redis服务器 隶属于Redis Master服务器的精确副本。Redis主从复制的配置是非常简单的,只需安装几个步骤即可。有关Redis更多信息,请参阅官方的Redis复制文档

对于生产环境,将数据复制到至少两个节点被认为是最佳的做法。这允许在环境故障的情况下恢复,这在您的应用程序用户基础增长以及体验尤为重要。它还允许您安全地与生产数据交互,而无需修改或影响性能。

1.规划(由于资源有限,暂用2台Server做为测试):

OS:CentOS 7.4
Redis 4.0.1
Redis Master 10.10.204.64
Redis Slave 10.10.204.65

2.配置Redis Replication之前首先需要安装Redis实例服务:

Linux Centos7 Redis 4.0.1 源码编译安装配置

3.主从互相添加Hosts:

主服务器:

 # echo “10.10.204.64 10-10-204-64” >> /etc/hosts
 # echo “10.10.204.65 10-10-204-65” >> /etc/hosts

从服务器:

 # echo “10.10.204.65 10-10-204-65” >> /etc/hosts
 # echo “10.10.204.64 10-10-204-64” >> /etc/hosts

4.Redis.conf 配置文件:

其实Slave的配置和Master基本一致,为什么这么说呢,当Master挂掉之后,那么Slave就担任了Master工作,所以一些参数就必须和Master相同,所以主从分别,只需要修改相应的pidfile,端口,日志文件名,并配上Master的地址和认证密码即可,生产环境亦是如此。

Master & Slave 通用配置:

 # vim /usr/local/redis/redis.conf
 port 6379 #端口信息
 daemonize yes #如果需要在后台运行,把该项改为yes
 pidfile /var/run/redis_6379.pid #主从PID路径
 logfile "/usr/local/redis/log/redis.log" #设置日志文件路径
 requirepass RenwoleQxl5qpKHrh9khuTW #设置设置256位连接密码
 masterauth RenwoleQxl5qpKHrh9khuTW #如果Master设置了密码,则Slave需要通过masterauth配置密码
 repl-diskless-sync yes #无硬盘复制功能通过以下配置
 repl-diskless-sync-delay 5
 maxmemory-policy volatile-lru #最大内存策略:如果达到内存限制了,Redis如何选择删除key.多选
 repl-ping-slave-period 10 #多少秒ping一次Master
 repl-timeout 60 #复制的超时时间,这个时间一定要大于ping的时间
 timeout 300 #客户端闲置多长时间后断开连接,默认为0关闭此功能
 min-slaves-to-write 3 #最小slave链接数默认为0
 min-slaves-max-lag 10 #最小的slave,最大延迟数默认为10
 dir /usr/local/redis-4.0.1 #自定义数据存储路径
 tcp-keepalive 60 #建议60,首先找到设置并将其设置为60秒
 appendonly yes 为了提高耐用性保证,可以启用仅追加文件的持久性,这助于最大程度减少系统故障时的数据丢失,同时IO的读写会付出相应的代价
 appendfilename "redis-staging-ao.aof"
 repl-backlog-ttl 3600 #在某些时候, master 不再连接 slaves,backlog 将被释放。如果设置为 0 意味着不释放 backlog
 maxclients 10000 #当连接数超过这个值时,redis 将不再接收其他连接请求,客户端尝试连接时将收到 error 信息

5.Master 配置:

bind 127.0.0.1 10.10.204.64

6.Slave 配置:

 bind 127.0.0.1 10.10.204.65
 slaveof 10.10.204.64 6379 #设置Master的IP与端口

注意:当从服务器执行了slaveof命令后,从服务器中原来的数据将清空,重新加载主服务器中的数据。

7.重新启动 Master & Slave 查看主从连接状态:

 # systemctl restart redis

8.查看 Master 角色以及Slave连接状态(注意:红色部分):

 # redis-cli
 10.10.204.64:6379> auth RenwoleQxl5qpKHrh9khuTW
 OK
 10.10.204.64:6379> info Replication

 # Replication
 role:master
 connected_slaves:1
 slave0:ip=10.10.204.65,port=6379,state=online,offset=101640,lag=1
 master_replid:cd78097afde482dd3ef18cf74ec66da7f2d7d140
 master_replid2:0000000000000000000000000000000000000000
 master_repl_offset:101640
 ...

9.查看Slave角色以及连接Master的状态(注意:红色部分):

 # redis-cli
 10.10.204.65:6379> auth RenwoleQxl5qpKHrh9khuTW
 OK
 10.10.204.65:6379> info Replication
 # Replication
 role:slave
 master_host:10.10.204.64
 master_port:6379
 master_link_status:up
 master_last_io_seconds_ago:7
 master_sync_in_progress:0
 slave_repl_offset:101906
 slave_priority:100
 slave_read_only:1
 connected_slaves:0
 ...

10.在Master节点上操作:

 10.10.204.64:6379> set key renwole.com
 OK
 10.10.204.64:6379> scan 0
 1) "0"
 2) 1) "name"
 2) "key"
 3) "name1"

11.检查Slave节点是否同步:

 10.10.204.65:6379> scan 0
 1) "0"
 2) 1) "key"
 2) "name1"
 3) "name"
 10.10.204.65:6379> get key
 "renwole.com"

Slave信息显示已经成功同步。

:Redis Replication 配置要求需要两台或多台redis实例之间保证端口相互通信,否则不能正常实现Redis复制。

12.Redis主从复制看起来完美无瑕,但由于Redis目前仅支持主从复制备份,而无法提供故障自动切换。所以这并不能满足我们的业务需求。

请参阅《Redis Sentinel冗余配置为Redis实例提供自动故障切换》高可用(HA)解决方案。

Redisson 缓存 Tomcat Redis Session Manager 变量

部署负载均衡时,使用一台Nginx作为前端服务器,多台Tomcat作为后端服务器。Nginx 会根据负载策略把请求分发给Tomcat服务器。默认Tomcat服务器的session(共享会话)是不可跨越服务器的,若是同一个用户的不同请求被分发到不同的Tomcat服务器,就会造成 session 变量丢失,需要用户重新登录。在讲解Redisson之前,我先来分析以下两种方案的Session会话共享。

方案一:Nginx原生 Upstream ip_hash

ip_hash 是根据请求的IP进行分发,对于同一个IP的请求会转发到同一台Tomcat服务器。

1.Nginx 配置如下:《Nginx 1.3 集群负载均衡器 反向代理安装配置优化

 upstream webservertomcat {
 ip_hash;
 server 10.10.204.63:8023 weight=1 max_fails=2 fail_timeout=2;
 server 10.10.204.64:8023 weight=1 max_fails=2 fail_timeout=2;
 #server 127.0.0.1:8080 backup;
 }
 server {
 listen 80;
 server_name www.myname.com myname.com;
 location / {
 proxy_pass //webservertomcat;
 proxy_redirect off;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 client_max_body_size 10m;
 client_body_buffer_size 128k;
 proxy_connect_timeout 90;
 proxy_send_timeout 90;
 proxy_read_timeout 90;
 proxy_buffer_size 4k;
 proxy_buffers 4 32k;
 proxy_busy_buffers_size 64k;
 proxy_temp_file_write_size 64k;
 add_header Access-Control-Allow-Origin *;
 }
 }

以上配置生产环境不建议使用,原因有二:

1.Nginx可能不是最前端的服务器,如Squid或CDN作为前端缓存,则Nginx实际是拿Squid或CDN服务器的IP地址,达不到根据客户端请求IP分流的效果。
2.有些机构是使用动态虚拟IP或有多个出口IP,用户访问会切换IP,则无法将某个用户的请求固定到同一个Tomcat服务器。

方案二:Nginx_Upstream_jvm_route

Nginx_upstream_jvm_route 是一个第三方 nginx 扩展模块,该模块通过 session cookie 实现 session 会话粘性,如果在cookie和url中并没有session,则这只是个简单的round-robin 负载均衡。

实现方法:用户初次请求分发到后端Server时;会把响应的Server标识添加到名称为JSESSIONID的cookie中,再次请求时;jvm_route看到session中有后端服务器的名称,它就把请求转到对应的服务器上。模块地址://code.google.com/archive/p/nginx-upstream-jvm-route/

1.Nginx配置如下:

 upstream tomcats_jvm_route {
 server 10.10.204.63:8023 srun_id=tomcat1;
 server 10.10.204.64:8023 srun_id=tomcat2;
 jvm_route $cookie_JSESSIONID|sessionid reverse;
 }

2.在多个Tomcat的server.xml添加配置如下:

 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">

3.配置后,请求的名称为JSESSIONID的cookie末尾会被添加服务器标识,如下:

 JSESSIONID=33775B80D8E0DB5AEAD61F86D6666C45.tomcat2;

4.生产环境不建议使用,原因:

4.1根据tomcat的特性,当server.xml配置文件中加了jvmRoute值后,会给sessionid加上jvmRoute值的后缀,根据这一特性 nginx_upstream_jvm_route对每次访问请求中的sessionId的值自动匹配对应的server。这样就会每次都访问都会到同一个Tomcat Server,这样就解决了访问不同tomcat节点session发生变化的问题。但是这种方式会出现一个问题,当一直被访问的Tomcat服务器宕机后,负载就会将用户分配到其他Server,这样就会造成session发生变化,从而就需要重新登录。

方案三:Tomcat Redis Session Manager

使用 Redis 共享 session,也就是本节中重点讲解的部分。以上两种方案均是将Session存储在Tomcat容器中,当请求从一个Tomcat转发到另一个Tomcat时,Session就会失效,因为Session在各个Tomcat中不能共享。如果使用Redis等缓存数据库系统存储Session,就可以在Tomcat实例之间实现Session共享。

Java框架redisson实现Tomcat会话管理器(Tomcat Session Manager),支持Apache Tomcat 6.x,7.x和8.x。配置方法如下:

1.编辑 TOMCAT_BASE/conf/context.xml 文件节点,添加以下内容:

<Manager className="org.redisson.tomcat.RedissonSessionManager"<Manager className="org.redisson.tomcat.RedissonSessionManager"  configPath="${catalina.base}/redisson-config.json" />

注:configPath – 是指的Redisson的JSON或YAML格式的配置文件路径。配置文件详见这里

2.拷贝相应的***两个***JAR包到指定的TOMCAT_BASE/lib目录下:

适用于 JDK 1.8+

redisson-all-3.5.0.jar

for Tomcat 6.x

redisson-tomcat-6-3.5.0.jar

for Tomcat 7.x

redisson-tomcat-7-3.5.0.jar

for Tomcat 8.x

redisson-tomcat-8-3.5.0.jar

3.按照 [Single instance mode]单实例模式的说明,创建编辑 Json 格式的 redisson-config.json 文件,添加以下内容,并将其上传到TOMCAT_BASE下,重载Tomcat服务即可。

{
 "singleServerConfig":{
 "idleConnectionTimeout":10000,
 "pingTimeout":1000,
 "connectTimeout":10000,
 "timeout":3000,
 "retryAttempts":3,
 "retryInterval":1500,
 "reconnectionTimeout":3000,
 "failedAttempts":3,
 "password":null,
 "subscriptionsPerConnection":5,
 "clientName":null,
 "address": "redis://127.0.0.1:6379",
 "subscriptionConnectionMinimumIdleSize":1,
 "subscriptionConnectionPoolSize":50,
 "connectionMinimumIdleSize":10,
 "connectionPoolSize":64,
 "database":0,
 "dnsMonitoring":false,
 "dnsMonitoringInterval":5000
 },
 "threads":0,
 "nettyThreads":0,
 "codec":null,
 "useLinuxNativeEpoll":false
 }

4.开始测试session。创建session.jsp并添加以下代码,上传到TOMCAT_BASE/webapps/ROOT/下。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>shared session</title>
</head>
<body>
<br>session id=<%=session.getId()%>
</body>
</html>

5.浏览器访问会出现以下会话信息

 session id=1D349A69E8F5A27E1C12DEEFC304F0DC

6.现在查看Redis中是否存储了该值,如果存在会话值,此时你重启Tomcat后,该会话值也不会发生变化。

 # redis-cli
 127.0.0.1:6379> keys *
 1) "redisson_tomcat_session:1D349A69E8F5A27E1C12DEEFC304F0DC"

已经存储成功。

如果是Redis集群或多实例模式,可参阅以下更多资料进行配置:

//github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks#144-tomcat-redis-session-manager
//github.com/redisson/redisson/wiki/2.-Configuration
//github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95 (中文文档)