消息队列: 把要传输的数据放在队列中
功能: 可以实现多个系统之间的解耦,异步,削峰/限流等
常用的消息队列应用: kafka,rabbitMQ,redis
消息队列主要分为两种,这两种模式Redis都支持
生产者/消费者模式 发布者/订阅者模式
在生产者/消费者(Producer/Consumer)模式下,上层应用接收到的外部请求后开始处理其当前步骤的操 作,在执行完成后将已经完成的操作发送至指定的频道(channel,逻辑队列)当中,并由其下层的应用监听 该频道并继续下一步的操作,如果其处理完成后没有下一步的操作就直接返回数据给外部请求,如果还 有下一步的操作就再将任务发布到另外一个频道,由另外一个消费者继续监听和处理。此模式应用广泛
模式介绍
生产者消费者模式下,多个消费者同时监听一个队列,但是一个消息只能被最先抢到消息的消费者消 费,即消息任务是一次性读取和处理,此模式在分布式业务架构中很常用,比较常用的消息队列软件还有RabbitMQ、Kafka、RocketMQ、ActiveMQ等。
队列介绍
队列当中的消息由不同的生产者写入,也会有不同的消费者取出进行消费处理,但是一个消息一定是只能被取出一次也就是被消费一次。
生产者发布消息
[root@redis ~]# redis-cli 127.0.0.1:6379> AUTH 123456 OK 127.0.0.1:6379> LPUSH channel1 msg1 #从管道的左侧写入 (integer) 1 127.0.0.1:6379> LPUSH channel1 msg2 (integer) 2 127.0.0.1:6379> LPUSH channel1 msg3 (integer) 3 127.0.0.1:6379> LPUSH channel1 msg4 (integer) 4 127.0.0.1:6379> LPUSH channel1 msg5 (integer) 5
查看队列所有消息
127.0.0.1:6379> LRANGE channel1 0 -1 1) "msg5" 2) "msg4" 3) "msg3" 4) "msg2" 5) "msg1"
消费者消费消息
127.0.0.1:6379> RPOP channel1 #从管道的右侧消费,用于消息的先进先出 "msg1" 127.0.0.1:6379> RPOP channel1 "msg2" 127.0.0.1:6379> RPOP channel1 "msg3" 127.0.0.1:6379> RPOP channel1 "msg4" 127.0.0.1:6379> RPOP channel1 "msg5" 127.0.0.1:6379> RPOP channel1 (nil)
再次验证队列消息
127.0.0.1:6379> LRANGE channel1 0 -1 (empty list or set) #队列中的消息已经被已全部消费完毕
模式简介
在发布者订阅者模式下,发布者将消息发布到指定的channel里面,凡是监听该channel的消费者都会收 到同样的一份消息,这种模式类似于是收音机的广播模式,即凡是收听某个频道的听众都会收到主持人发布的相同的消息内容。此模式常用语群聊天、群通知、群公告等场景
Publisher:发布者
Subscriber:订阅者
Channel:频道
[root@redis ~]# redis-cli 127.0.0.1:6379> AUTH 123456 OK 127.0.0.1:6379> SUBSCRIBE channel1 #订阅者事先订阅指定的频道,之后发布的消息才 能收到 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "channel1" 3) (integer) 1
发布者发布消息
127.0.0.1:6379> PUBLISH channel1 test1 #发布者发布消息 (integer) 2 #订阅者个数 127.0.0.1:6379> PUBLISH channel1 test2 (integer) 2
各个订阅者都能收到消息
订阅多个频道
#订阅指定的多个频道 127.0.0.1:6379> SUBSCRIBE channel1 channel2
订阅所有频道
127.0.0.1:6379> PSUBSCRIBE * #支持通配符*
订阅匹配的频道
127.0.0.1:6379> PSUBSCRIBE chann* #匹配订阅多个频道
取消订阅
127.0.0.1:6379> unsubscribe channel1 1) "unsubscribe" 2) "channel1" 3) (integer) 0
虽然Redis可以实现单机的数据持久化,但无论是RDB也好或者AOF也好,都解决不了单点宕机问题,即
一旦单台 redis服务器本身出现系统故障、硬件故障等问题后,就会直接造成数据的丢失
此外,单机的性能也是有极限的,因此需要使用另外的技术来解决单点故障和性能扩展的问题。
redis 主从复制架构
主从模式(master/slave),可以实现Redis数据的跨主机备份。 程序端连接到高可用负载的VIP,然后连接到负载服务器设置的Redis后端real server,此模式不需要在 程序里面配 置Redis服务器的真实IP地址,当后期Redis服务器IP地址发生变更只需要更改redis 相应的 后端real server即可, 可避免更改程序中的IP地址设置。
主从复制特点
一个master可以有多个slave 一个slave只能有一个master 数据流向是从master到slave单向的
主从复制实现
Redis Slave 也要开启持久化并设置和master同样的连接密码,因为后期slave会有提升为master的可 能,Slave 端切换master同步后会丢失之前的所有数据,而通过持久化可以恢复数据 一旦某个Slave成为一个master的slave,Redis Slave服务会清空当前redis服务器上的所有数据并将 master的数据导入到自己的内存,但是如果只是断开同步关系后,则不会删除当前已经同步过的数据。 当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。否则的话,由于延迟等问题,部署的主 节点Redis服务应该要避免自动启动。
参考案例: 导致主从服务器数据全部丢失
1.假设节点A为主服务器,并且关闭了持久化。并且节点B和节点c从节点A复制数据 2.节点A崩溃,然后由自动拉起服务重启了节点A.由于节点A的持久化被关闭了,所以重启之后没有任何数据 3.节点B和节点c将从节点A复制数据,但是A的数据是空的,于是就把自身保存的数据副本删除。
在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可
用性,也是非常危险的。因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时间间隔内没
有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动启动。
启用主从同步
默认redis 状态为master,需要转换为slave角色并指向master服务器的IP+PORT+Password
在从节点执行REPLICAOF MASTER_IP PORT 指令可以启用主从同步复制功能,早期版本使用 SLAVEOF指令(注意防火墙问题Firewalld)
127.0.0.1:6379> REPLICAOF MASTER_IP PORT 127.0.0.1:6379> CONFIG SET masterauth <masterpass>
范例:
#在mater上设置key1 [root@centos8 ~]# redis-cli 127.0.0.1:6379> AUTH 123456 OK 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "" 127.0.0.1:6379> config set requirepass 123456 OK 127.0.0.1:6379> config get requirepass (error) NOAUTH Authentication required. 127.0.0.1:6379> auth 123456 OK 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "123456" 127.0.0.1:6379> INFO replication # Replication role:master connected_slaves:0 ... 127.0.0.1:6379> SET key1 v1-master OK 127.0.0.1:6379> KEYS * 1) "key1" 127.0.0.1:6379> GET key1 "v1-master" 127.0.0.1:6379>
范例:
#以下都在slave上执行,登录 [root@centos8 ~]# redis-cli 127.0.0.1:6379> info NOAUTH Authentication required. 127.0.0.1:6379> AUTH 123456 OK 127.0.0.1:6379> INFO replication #查看当前角色默认为master # Replication role:master connected_slaves:0 ... 127.0.0.1:6379> SET key1 v1-slave-18 OK 127.0.0.1:6379> KEYS * 1) "key1" 127.0.0.1:6379> GET key1 "v1-slave-18" 127.0.0.1:6379> #在第二个slave,也设置相同的key1,但值不同 127.0.0.1:6379> KEYS * 1) "key1" 127.0.0.1:6379> GET key1 "v1-slave-28" 127.0.0.1:6379> 127.0.0.1:6379> INFO replication # Replication role:master ... #在slave上设置master的IP和端口,4.0版之前的指令为slaveof 127.0.0.1:6379> REPLICAOF 172.31.0.8 6379 #仍可使用SLAVEOF MasterIP Port OK #在slave上设置master的密码,才可以同步 127.0.0.1:6379> CONFIG SET masterauth 123456 OK 127.0.0.1:6379> INFO replication # Replication #角色变为slave role:slave master_host:172.31.0.8 #指向master master_port:6379 master_link_status:up #查看已经同步成功 127.0.0.1:6379> GET key1 "v1-master" #在master上可以看到所有slave信息 127.0.0.1:6379> INFO replication # Replication role:master connected_slaves:2 slave0:ip=172.31.0.18,port=6379,state=online,offset=112,lag=1 #slave信息 slave1:ip=172.31.0.28,port=6379,state=online,offset=112,lag=1
在从节点执行 REPLIATOF NO ONE 指令可以取消主从复制
#取消复制,在slave上执行REPLIATOF NO ONE,会断开和master的连接不再主从复制, 但不会清除slave 上已有的数据 127.0.0.1:6379> REPLICAOF no one
在 master 上观察日志
[root@centos8 ~]# tail /var/log/redis/redis.log 24402:M 06 Oct 2021 09:09:16.448 * Replica 172.31.0.18:6379 asks for synchronization
在 slave 节点观察日志
[root@centos8 ~]# tail -f /var/log/redis/redis.log 24395:S 06 Oct 2021 09:09:16.411 * Connecting to MASTER 172.31.0.8:6379 24395:S 06 Oct 2021 09:09:16.412 * MASTER <-> REPLICA sync started
范例: (CentOS8 注意防火墙问题)
[root@centos8 ~]# vim /etc/redis.conf ....... # replicaof <masterip> <masterport> replicaof 172.31.0.8 6379 #指定master的IP和端口号 # If the master is password protected (using the "requirepass" configuration # directive below) it is possible to tell the replica to authenticate before # starting the replication synchronization process, otherwise the master will # refuse the replica request. # masterauth <master-password> masterauth 123456 #如果密码需要设置 ....... [root@centos8 ~]# systemctl restart redis
master和slave查看状态
#在master上查看状态 127.0.0.1:6379> info replication # Replication role:master connected_slaves:1 slave0:ip=172.31.0.18,port=6379,state=online,offset=1104403,lag=0 #在slave上查看状态 127.0.0.1:6379> get key1 #同步成功后,slave上的key信息丢失,从master复制过来新的值 "v1-master" 127.0.0.1:6379> INFO replication # Replication role:slave master_host:172.31.0.8 master_port:6379 master_link_status:up ... #停止master的redis服务:systemctl stop redis,在slave上可以观察到以下现象 127.0.0.1:6379> INFO replication # Replication role:slave master_host:172.31.0.8 master_port:6379 master_link_status:down #显示down,表示无法连接master
slave 观察日志
[root@centos8 ~]# tail -f /var/log/redis/redis.log 24592:S 20 Feb 2021 12:03:58.792 * Connecting to MASTER 172.31.0.8:6379 24592:S 20 Feb 2021 12:03:58.792 * MASTER <-> REPLICA sync started
Master日志
[root@centos8 ~]# tail /var/log/redis/redis.log 11846:M 20 Feb 2021 12:11:35.171 * DB loaded from disk: 0.000 seconds 11846:M 20 Feb 2021 12:11:35.171 * Ready to accept connections
slave 状态只读无法写入数据
127.0.0.1:6379> set key1 v1-slave (error) READONLY You can't write against a read only replica.
主从复制故障恢复过程介绍
当 slave 节点故障时,将Redis Client指向另一个 slave 节点即可,并及时修复故障从节点
需要提升slave为新的master
master故障后,只能手动提升一个slave为新master,不支持自动切换。
之后将其它的slave节点重新指定新的master为master节点
Master的切换会导致master_replid发生变化,slave之前的master_replid就和当前master不一致从而会引发所有 slave的全量同步。
假设当前主节点172.31.0.8故障,提升172.31.0.18为新的master
127.0.0.1:6379> info replication # Replication role:slave master_host:172.31.0.18 master_port:6379 master_link_status:up
停止slave同步并提升为新的master
#将当前 slave 节点提升为 master 角色 127.0.0.1:6379> REPLICAOF NO ONE #旧版使用SLAVEOF no one OK (5.04s) 127.0.0.1:6379> info replication # Replication role:master connected_slaves:0
测试能否写入数据:
127.0.0.1:6379> set keytest1 vtest1 OK
修改所有slave 指向新的master节点
#修改172.31.0.28节点指向新的master节点172.31.0.18 127.0.0.1:6379> SLAVEOF 172.31.0.8 6379 OK 127.0.0.1:6379> set key100 v100 (error) READONLY You can't write against a read only replica. #查看日志 [root@centos8 ~]# tail -f /var/log/redis/redis.log 1762:S 20 Feb 2021 13:28:21.943 # Connection with master lost. 1762:S 20 Feb 2021 13:28:21.943 * Caching the disconnected master state.
在新master可看到slave
#在新master节点172.31.0.18上查看状态 127.0.0.1:6379> INFO replication # Replication role:master connected_slaves:1 slave0:ip=172.31.0.28,port=6379,state=online,offset=4606,lag=0 ...
即实现基于Slave节点的Slave
master和slave1节点无需修改,只需要修改slave2及slave3指向slave1做为mater即可
#在slave2和slave3上执行下面指令 127.0.0.1:6379> REPLICAOF 172.31.0.18 6379 OK 127.0.0.1:6379> CONFIG SET masterauth 123456
在 master 设置key,观察是否同步
#在master新建key 127.0.0.1:6379> set key2 v2 OK 127.0.0.1:6379> get key2 "v2" #在slave1和slave2验证key 127.0.0.1:6379> get key2 "v2" #在slave1和slave2都无法新建key 127.0.0.1:6379> set key3 v3 (error) READONLY You can't write against a read only replica.
在中间那个slave查看状态
127.0.0.1:6379> INFO replication # Replication role:slave master_host:172.31.0.8 master_port:6379 master_link_status:up master_last_io_seconds_ago:8 #最近一次与master通信已经过去多少秒。 master_sync_in_progress:0 #是否正在与master通信。 slave_repl_offset:4312 #当前同步的偏移量 slave_priority:100 #slave优先级,master故障后值越小越优先同步。 slave_read_only:1 connected_slaves:1 slave0:ip=172.31.0.28,port=6379,state=online,offset=4312,lag=0 #slave的slave节点
主从复制过程
Redis主从复制分为全量同步和增量同步
首次主从同步是全量同步,主从同步可以让从服务器从主服务器同步数据,而且从服务器还可再有其它 的从服务器,即另外一台redis服务器可以从一台从服务器进行数据同步,redis 的主从同步是非阻塞 的,master收到从服务器的psync(2.8版本之前是SYNC)命令,会fork一个子进程在后台执行bgsave命 令,并将新写入的数据写入到一个缓冲区中,bgsave执行完成之后,将生成的RDB文件发送给slave,然 后master再将缓冲区的内容以redis协议格式再全部发送给slave,slave 先删除旧数据,slave将收到后的 RDB文件载入自己的内存,再加载所有收到缓冲区的内容 从而这样一次完整的数据同步 Redis全量复制一般发生在Slave首次初始化阶段,这时Slave需要将Master上的所有数据都复制一份。 全量同步之后再次需要同步时,从服务器只要发送当前的offset位置(等同于MySQL的binlog的位置)给主 服务器,然后主服务器根据相应的位置将之后的数据(包括写在缓冲区的积压数据)发送给从服务器,再次 将其保存到从节点内存即可。
具体主从同步过程如下:
1)从服务器连接主服务器,发送PSYNC命令 2)主服务器接收到PSYNC命令后,开始执行BGSAVE命令生成RDB快照文件并使用缓冲区记录此后执行的所有 写命令 3)主服务器BGSAVE执行完后,向所有从服务器发送RDB快照文件,并在发送期间继续记录被执行的写命令 4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照至内存 5)主服务器快照发送完毕后,开始向从服务器发送缓冲区中的写命令 6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令 7)后期同步会先发送自己slave_repl_offset位置,只同步新增加的数据,不再全量同步
复制缓冲区(环形队列)配置参数:
#复制缓冲区大小,建议要设置足够大 repl-backlog-size 1mb #Redis同时也提供了当没有slave需要同步的时候,多久可以释放环形队列: repl-backlog-ttl 3600
第一次全量复制不可避免,后续的全量复制可以利用小主节点(内存小),业务低峰时进行全量 节点运行ID不匹配:主节点重启会导致RUNID变化,可能会触发全量复制,可以利用故障转移,例如哨 兵或集群,而从节点重启动,不会导致全量复制 复制积压缓冲区不足: 当主节点生成的新数据大于缓冲区大小,从节点恢复和主节点连接后,会导致全量复制.解决方法将repl-backlog-size 调大
单主节点复制风暴 当主节点重启,多从节点复制 解决方法:更换复制拓扑 单机器多实例复制风暴 机器宕机后,大量全量复制 解决方法:主节点分散多机器
Redis在2.8版本之前没有提供增量部分复制的功能,当网络闪断或者slave Redis重启之后会导致主从之间的全量同步,即从2.8版本开始增加了部分复制的功能。
repl-diskless-sync no # 是否使用无盘同步RDB文件,默认为no,no为不使用无盘,需要将RDB文件保 存到磁盘后再发送给slave,yes为支持无盘,支持无盘就是RDB文件不需要保存至本地磁盘,而且直接通过 socket文件发送给slave repl-diskless-sync-delay 5 #diskless时复制的服务器等待的延迟时间 repl-ping-slave-period 10 #slave端向server端发送ping的时间间隔,默认为10秒 repl-timeout 60 #设置主从ping连接超时时间,超过此值无法连接,master_link_status显示为down, 并记录错误日志 repl-disable-tcp-nodelay no #是否启用TCP_NODELAY,如设置成yes,则redis会合并小的TCP包从 而节省带宽, 但会增加同步延迟(40ms),造成master与slave数据不一致,假如设置成no,则redis master会立即发送同步数据,没有延迟,yes关注网络性能,no关注redis服务中的数据一致性 repl-backlog-size 1mb #master的写入数据缓冲区,用于记录自上一次同步后到下一次同步过程中间的 写入命令,计算公式:repl-backlog-size = 允许从节点最大中断时长 * 主实例offset每秒写入量,比 如master每秒最大写入64mb,最大允许60秒,那么就要设置为64mb*60秒=3840MB(3.8G),建议此值是设 置的足够大 repl-backlog-ttl 3600 #如果一段时间后没有slave连接到master,则backlog size的内存将会被释 放。如果值为0则 表示永远不释放这部份内存。 slave-priority 100 #slave端的优先级设置,值是一个整数,数字越小表示优先级越高。当master故障 时将会按照优先级来选择slave端进行恢复,如果值设置为0,则表示该slave永远不会被选择。 min-replicas-to-write 1 #设置一个master的可用slave不能少于多少个,否则master无法执行写 min-slaves-max-lag 20 #设置至少有上面数量的slave延迟时间都大于多少秒时,master不接收写操作(拒绝写入)
即配置的master密码不对,导致验证不通过而无法建立主从同步关系。
[root@centos8 ~]# tail -f /var/log/redis/redis.log 24930:S 20 Feb 2021 13:53:57.029 * Connecting to MASTER 172.31.0.8:6379 24930:S 20 Feb 2021 13:53:57.030 * MASTER <-> REPLICA sync started 24930:S 20 Feb 2021 13:53:57.030 * Non blocking connect for SYNC fired the event. 24930:S 20 Feb 2021 13:53:57.030 * Master replied to PING, replication can continue... 24930:S 20 Feb 2021 13:53:57.031 # Unable to AUTH to MASTER: -ERR invalid password
不同的redis 大版本之间存在兼容性问题,比如:3和4,4和5之间,因此各master和slave之间必须保持版本一致
在开启了安全模式情况下,没有设置bind地址或者密码
[root@centos8 ~]# vim /etc/redis.conf #bind 127.0.0.1 #将此行注释 [root@centos8 ~]# systemctl restart redis [root@centos8 ~]# ss -ntl #可以本机登录 [root@centos8 ~]# redis-cli 127.0.0.1:6379> KEYS * (empty list or set)
主从节点的maxmemory不一致,主节点内存大于从节点内存,主从复制可能丢失数据
rename-command 命令不一致,如在主节点定义了fushall,flushdb,从节点没定义,结果执行flushdb,不同步
#master有一个rename-command flushdb "king",而slave没有这个配置,则同步时从节点可以看到 以下同步错误 3181:S 21 Oct 2021 17:34:50.581 # == CRITICAL == This replica is sending an error to its master: 'unknown command `king`, with args beginning with: ' after processing the command '<unknown>'