组件
FastDFS
原理
Tracker server:
作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。Tracker需要管理的信息也都放在内存中,并且里面所有的Tracker都是对等的(每个节点地位相等),很容易扩展。
Storage server:
作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上, Storageserver没有实现自己的文件系统而是利用操作系统 的文件系统来管理文件。可以将 storage 称为存储服务器;实际是用来保存文件, Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。即上传一个文件到其中一个storage,那么同组内其他的storage会进行文件的同步。
Storage的数据同步,使用binlog:
- Storage Server会为文件系统中的每一个Tracker启动一个线程
1)用于与Tracker之间通信。
2)实现心跳反馈机制,默认每30秒发送向Tracker发送一次心跳包,包含同组内其他Storage信息。
3)通过这一线程从Tracker中获取Storage列表信息 - Storage会为同组的每一个Storage开启一个线程
1)从Tracer获取到同组内其他Storage Server信息,根据binlog向这些Storage Server同步数据(阻塞方式)。
2)如果组内有三个Storage,那么每个Storage都会启动两个线程用于向其他Storage同步数据。
3)打开/data/sync的mark文件,读取目标Storage_Server_IP.mark文件,获取对应的binlog文件以及同步的offset。
4)在对应的binlog文件中找到offset对应的文件操作,如果这已操作是源操作(CADT),则将操作将数据向外同步。同步完成后更新mark文件的offset值
配置
配置内容如下:
# storage.conf
group_name = group1
tracker_server = localServer:22122
tracker_server = peerServer1:22122
tracker_server = peerServer2:22122
...
# tracker.conf
store_group = group1
测试
测试配置如下:
# 237
## /etc/hosts
10.10.10.237 localServer
10.10.10.238 peerServer
10.10.10.104 server104
## tracker.conf
store_group = group1
## storage.conf
group_name = group1
tracker_server = localServer:22122
tracker_server = peerServer:22122
tracker_server = server104:22122
# 238
## /etc/hosts
10.10.10.238 localServer
10.10.10.237 peerServer
10.10.10.104 server104
## tracker.conf
store_group = group1
## storage.conf
group_name = group1
tracker_server = localServer:22122
tracker_server = peerServer:22122
tracker_server = server104:22122
# 104
## /etc/hosts
10.10.10.104 localServer
10.10.10.237 server237
10.10.10.238 server238
## tracker.conf
store_group = group1
## storage.conf
group_name = group1
tracker_server = localServer:22122
tracker_server = server237:22122
tracker_server = server238:22122
使用fdfs自带的client进行文件的上传、下载和删除,命令如下:
# 上传
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf filePath
# 下载
/usr/bin/fdfs_download_file /etc/fdfs/client.conf fileId
# 删除
/usr/bin/fdfs_delete_file /etc/fdfs/client.conf fileId
从237上传文件test,然后关闭237的storage,下载该文件,下载成功;
继续关闭238的storage,下载该文件,下载成功;
继续关闭104的storage,下载该文件,下载失败。说明该文件已经被同步到了各个storage中。
将所有storage重启,删除该文件,下载该文件,下载失败;分别关闭237、238、104进行下载测试,均下载失败。说明该文件已经被各storage删除了。
方案下的特殊处理
因为FastDFS是没有主备概念的,同一个group下的所有storage都会互相进行数据的同步,因此不需要通过代码对FastDFS进行特殊的处理,只需要进行开局配置时,配置多个正确的tracker以及配置正确的组即可。
Keepalived
原理
keepalived虚拟IP切换的原理如下:
1、正常情况下,用户通过虚拟IP是直接访问到Keepalived-Master的(没有成为Master的就是Backup);
2、成为Master的Keepalived,会每秒向所有的Backup发送VRRP包,通告自己是主,且运行正常;
3、当Master因为网络原因或者是别的原因导致与集群断开之后,Backup会在3.6秒左右(以优先级100为例,计算公式为3 × 1 + 256 × (256 - 100))认定Master宕机;
4、如果是多播的情况下,Master宕机,那么剩余的Backup要通过选举产生新的Master;如果是单播,则由剩下的Bakcup直接作为新的Master。
选举机制如下:
keepalived中优先级高的节点为MASTER。MASTER其中一个职责就是响应VIP的arp包,将VIP和mac地址映射关系告诉局域网内其他主机,同时,它还会以多播的形式(默认目的地址224.0.0.18)向局域网中发送VRRP通告,告知自己的优先级。网络中的所有BACKUP节点只负责处理MASTER发出的多播包,在抢占模式下,当发现MASTER的优先级没自己高,或者没收到MASTER的VRRP通告时,BACKUP将自己切换到MASTER状态,然后做MASTER该做的事:响应arp包和发送VRRP通告
如果在同一个广播域的同一个VRRP组内出现了两台Master路由器,那么它们收到对方发送的VRRP通
告报文之后,将比较自己与对方的优先级,优先级的值更大的设备胜出,继续保持Master状态,而
竞争失败的路由器则切换到Backup状态。如果这两台Master路由器的优先级相等,那么接口IP地址
更大的路由器接口将会保持Master状态,而另一台设备则切换到Backup状态。
上面提到了一个抢占模式,这种模式主要是针对主机崩溃,集群已经重新选出新的主机,并且原来的主机重新上线后重新争夺主机、成为主机的情况。这种情况比较适合需要崩溃的主机重新上线做主机的情况。也就是本模式是某主机节点在崩溃后,集群新选出了主机。但是崩溃的节点恢复后,又触发了选举,最终原节点再次成为主机的过程。
那么就还有非抢占模式,这种模式主要是针对主机崩溃,集群已经重新选出新的主机,并且原来的主机重新上线后并不争夺主机的情况。这种模式适合那些倾向于认为崩溃的主机即便上线还是会出现崩溃的场景。
这种模式的核心思想是将所有节点的优先级(priority)值设为相同,当两个节点的优先级相同时,以节点发送VRRP通告的IP作为比较对象,IP较大者为MASTER。
因此这种模式下,当主机崩溃时,所有从机中IP地址最大的那个节点一定会成为主机,并且原来的主机
上线后不会再次触发选举,原来的主机也就成为了从机。
配置
因为需要保证当主机崩溃时,从机接替成功主机的工作之后,主机上线并成为新的从机,那么我们就要选择非抢占模式,配置如下:
vrrp_instance VI_1 {
state BACKUP ## 都设置为BACKUP,首先启动的作为Master
nopreempt ## 非抢占模式
interface enp0s3 ## 虚拟IP绑定网卡
virtual_router_id 99 ## 虚拟路由id,这个id在整个局域网中需要唯一
priority 100 ## 优先级
advert_int 1 ## VRRP广播的时间间隔
authentication {
auth_type AH ## keepalived认证,类型有AH加密和PASS明文
auth_pass 123456 ## 密码,集群中保持一致,且取前8个字符
}
unicast_src_ip 10.10.10.237 ## 绑定IP
unicast_peer {
10.10.10.238 ## 对端IP
10.10.10.104
}
virtual_ipaddress { ## 虚拟IP
10.10.10.239
}
}
测试
# 237
vrrp_instance VI_1 {
state BACKUP
nopreempt
interface enp0s3
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type AH
auth_pass 123456
}
unicast_src_ip 10.10.10.237
unicast_peer {
10.10.10.238
10.10.10.104
}
virtual_ipaddress {
10.10.10.239
}
track_script {
onos_check ## 执行 onos 监控的服务
}
}
# 238
vrrp_instance VI_1 {
state BACKUP
nopreempt
interface enp0s3
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type AH
auth_pass 123456
}
unicast_src_ip 10.10.10.238
unicast_peer {
10.10.10.237
10.10.10.104
}
virtual_ipaddress {
10.10.10.239
}
}
# 104
vrrp_instance VI_1 {
state BACKUP
nopreempt
interface enp0s3
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type AH
auth_pass 123456
}
unicast_src_ip 10.10.10.104
unicast_peer {
10.10.10.238
10.10.10.237
}
virtual_ipaddress {
10.10.10.239
}
}
先启动104,再启动237和238,虚拟IP239偏移至104;说明集群在没有MASTER的情况下,先启动的成为MASTER,在非抢占模式下,后启动的就算是IP更大的也只能变成BACKUP。
关闭104,虚拟IP偏移至238,开启104,虚拟IP依旧在238;关闭239,虚拟IP飘移至237,开启238,虚拟IP依旧在237;说明在选举时,当优先级相同时,IP较大的将会作为新的MASTER,而原MASTER重新接入之后,只能成为BACKUP。
方案下的特殊处理
主动主备切换
将当前的MASTER-keepalived以及比指定成为新Master的keepalived的IP更大的keepalived关闭,然后等待完成故障转移,再将备关闭的keepalived开启即可。
例如目前104为MASTER,要使得238成为主,只需要104关闭,等待故障转移成功再将其开启即可;
目前238为MASTER,要使得104成为主,需要将238和237关闭,等待故障转移成功再将其开启即可。
被动主备切换
当MASTER宕机之后,剩余健康的keepalived中,IP较大的将会成为新的主MASTER,原MASTER重新上线之后将会成为BACKUP。
MariaDB
原理
Mysql主从复制原理:
Mysql的主从复制中主要有三个线程:master(binlog dump thread)、slave(I/O thread 、SQL thread)
,Master一条线程和Slave中的两条线程。
master(binlog dump thread)
主要负责Master库中有数据更新的时候,会按照binlog
格式,将更新的事件类型写入到主库的binlog
文件中。并且,Master会创建log dump
线程通知Slave主库中存在数据更新。
I/O thread
线程在Slave中创建,该线程用于请求Master,Master会返回binlog的名称以及当前数据更新的位置、binlog文件位置的副本。然后,将binlog
保存在 「relay log(中继日志)」 中,中继日志也是记录数据更新的信息。
SQL线程也是在Slave中创建的,当Slave检测到中继日志有更新,就会将更新的内容同步到Slave数据库中,这样就保证了主从的数据的同步。
多主互备的话就是,为每个数据库都配置其他数据库的主从复制即可。
配置
[mysqld]
# server-id不能相同
server-id = 100
# bin_log的地址
log_bin = /var/lib/mysql/mysql-bin.log
# id自增幅度,有几台就配置成几
auto-increment-increment = 3
# id初始值,与auto-increment-increment一起就能保证id两台mysql插入的id不同,每台需要配置不一样
auto-increment-offset = 1
# 复制时忽略数据库及表,建议了解该字段含义之后进行配置,这里配置成完全忽略复制时的错误
slave-skip-errors = all
# 定义relay_log的位置和名称,如果值为空,则默认位置在数据文件的目录,文件名为host_name-relay-bin.nnnnnn,如果服务器的主机名不是localhost的话不配置会有问题,建议配置
relay-log = localServer-relay-log
relay_log_index = localServer-relay-log.index
# 主备连接断开超时时间,超过该时间之后,slave进行重连,如果该值太大的话(默认1小时),那么当Master的dump log线程关闭之后到1小时这段期间内,就算slave连接回来也需要过1小时才能进行正常的同步
slave-net-timeout = 300
slave相关命令如下:
## 关闭名称为xxxx的同步
STOP SLAVE 'xxxx';
## 设置slave配置
change master 'xxxx' to MASTER_HOST='host', master_port=3306,master_user='rep',master_password='password',MASTER_LOG_FILE='mysql-bin.xxxxxx', MASTER_LOG_POS=x for channel 'xxxx';
## 开启名称为peerServer的同步
START SLAVE 'xxxxx';
## 同时开启和关闭所有slave
start all slaves;
stop all slaves;
## 查看所有slave状态
show all slaves status;
## 查看单个slave状态
show slave 'xxxx' status;
测试
配置文件如下:
# 237
[mysqld]
server-id = 100
auto-increment-increment = 3
auto-increment-offset = 1
# 238
[mysqld]
server-id = 200
auto-increment-increment = 3
auto-increment-offset = 2
# 104
[mysqld]
server-id = 300
auto-increment-increment = 3
auto-increment-offset = 3
配置完成后,为同一张表插入两条数据,查看id与预期相同,即237的为1和4;238为2和5;104为3和6。
slave开启命令如下(先通过show master status查看各个数据库的binlog位置):
本次测试binlog位置如下:
# 237
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000014 | 587 | | |
+------------------+----------+--------------+------------------+
# 238
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000008 | 1011 | | |
+------------------+----------+--------------+------------------+
# 104
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000008 | 2114 | | |
+------------------+----------+--------------+------------------+
各数据库slave配置如下:
# 237
# 关闭所有slaves
stop all slaves;
## 237->238
change master 'peerServer' to MASTER_HOST='peerServer', master_port=3306, master_user='rep', master_password='zhkjmysql@002396', MASTER_LOG_FILE='mysql-bin.000008', MASTER_LOG_POS=1011;
## 237->104
change master 'server104' to MASTER_HOST='server104', master_port=3306, master_user='rep', master_password='zhkjmysql@002396', MASTER_LOG_FILE='mysql-bin.000008', MASTER_LOG_POS=2114;
# 开启所有slaves
start all slaves;
# 238
# 关闭所有slaves
stop all slaves;
## 238->237
change master 'peerServer' to MASTER_HOST='peerServer', master_port=3306, master_user='rep', master_password='zhkjmysql@002396', MASTER_LOG_FILE='mysql-bin.000014', MASTER_LOG_POS=587;
## 237->104
change master 'server104' to MASTER_HOST='server104', master_port=3306, master_user='rep', master_password='zhkjmysql@002396', MASTER_LOG_FILE='mysql-bin.000008', MASTER_LOG_POS=2114;
# 开启所有slaves
start all slaves;
# 104
stop all slaves;
## 104->237
change master 'server237' to MASTER_HOST='server237', master_port=3306, master_user='rep', master_password='zhkjmysql@002396', MASTER_LOG_FILE='mysql-bin.000014', MASTER_LOG_POS=587;
## 104->238
change master 'server238' to MASTER_HOST='server238', master_port=3306, master_user='rep', master_password='zhkjmysql@002396', MASTER_LOG_FILE='mysql-bin.000008', MASTER_LOG_POS=1011;
# 开启所有slaves
start all slaves;
开启之后,分别在三个数据库中分别插入数据,查看各数据库均完成了数据的同步。
方案下的特殊处理
因为数据库是互备的,因此没有主备的概念,对于其使用在主备方案下,不要需要通过代码进行主备切换的操作;但是需要去检测slave的状态是否正常,并在其slave不正常时进行告警处理以及恢复slave异常的处理。
Redis
原理
Redis的主从复制:
1、同一个Master可以同步多个Slaves。
2、Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
3、Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
4、Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。
5、在Slave启动并连接到Master之后,它将主动发送一个SYNC命令。此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其存盘并加载到内存中。即连上之后,先进行一次全量同步。
6、此后,Master继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。后面进行增量同步。
7、 如果Master和Slave之间的链接出现断连现象,Slave可以自动重连Master,但是在连接成功之后,一次完全同步将被自动执行。
发现故障
主观下线(subjectively down,SDOWN):当前 Sentinel 实例认为某个 redis 服务为”不可用”状态。Sentinel 向 redis master 数据节点发送消息后 30s(down-after-milliseconds) 内没有收到有效回复(+PONG、-LOADING 或者 -MASTERDOWN),Sentinel 会将 master 标记为下线
**客观下线(objectively down,ODOWN):**多个 Sentinel 实例都认为 master 处于 SDOWN 状态,那么此时 master 将处于 ODOWN, ODOWN可以简单理解为master已经被集群确定为”不可用”,将会开启故障转移机制。向其他 sentinel 节点发送 sentinel is-master-down-by-addr
消息询问数据节点情况,得知达到 quorum 数量的 sentinel 节点认为数据节点已经下线
故障处理–选举
就是选举出新的Leader-Sentinel,选举过程如下:
1、发现主库客观下线的哨兵节点(这里称为A)向每个哨兵节点发送命令要求对方选举自己为领头哨兵(leader);
2、如果目标哨兵没有选举过其他人,则同意将A选举为领头哨兵;
3、如果A发现有超过半数且超过quorum参数值的哨兵节点同意选自己成为领头哨兵,则A哨兵成功选举为领头哨兵。
4、当有多个哨兵节点同时参与领头哨兵选举时,出现没有任何节点当选可能,此时每个参选节点等待一个随机时间进行下一轮选举,直到选出领头哨兵。
故障处理–转移(切换 Redis master 数据节点)
- sentinel master 选择合适的 redis slave 成为 master
slave 选择标准:健康的节点:在线的、最近成功通信过的(5s 内回复过PING
命令)、数据比较新的(与 master 失联时间不超过 10*down-after-milliseconds)、slave-priority(slave节点优先级)最高的slave节点(越小越高,master节点的优先级为0,因此如果设置为0的话永远不会被选为主)、复制偏移量最大的 slave 节点(复制的最完整)、选择 runId 最小的 slave 节点(启动最早的节点) - 执行
SLAVEOF no one
(不会删除已有数据,只是不再接受主节点新的数据变化) 命令让其成为新的 master 节点。每秒 Sentinel 向其发送一次INFO
命令,直到成功变为 master - 向剩余的 slave 节点发送
SLAVEOF 新master
命令,让他们成为新 master 节点的 slave 节点 - 让剩余的 slave 复制新 master 的数据,通过配置
sentinel parallel-syncs
(sentinel.conf) 规定了每次向新的主节点发起复制操作的从节点个数,parallel-syncs 取值越大,slave 完成复制的时间越快,但是对主节点的网络负载、硬盘负载造成的压力也越大 - 更新原来master 节点配置为 slave 节点,并保持对其进行关注,一旦这个节点重新恢复正常后,会命令它去复制新的master节点信息
- 全部故障转移工作完成后,Leader Sentinel 就会推送 +switch-master 消息,同时重置 master,重置操作会释放掉原来 master 全部的 slave 对象和监听该 master 的其他 Sentinel 对象,然后创建出新的 slave 对象。故障迁移过程中 slave 能否返回数据给客户端取决于
slave-serve-stale-data
(redis.conf) - 持续关注旧的 master,并在他重新上线后将它设置为新 master 的 slave
在 sentinel 中执行 sentinel failover master
可以强制该 sentinel 节点执行故障转移,不与其他节点进行选举(跳过leader的选举过程),因为最先使用优先级进行选择新master,那么可以使用以下命令来配置redis的slave优先级
## 默认是100,这里配置成50
config set slave-priority 50
## 其他可配置项目可以通过以下命令查看
config get *
## 配置的信息如果想要保存到配置文件中,可以使用以下命令,进行redis主备转移的时候会默认执行一次这个
config rewrite
配置
redis.conf配置内容如下:
# 开启后台启动,默认为no
daemonize yes
# 新增密码
requirepass "password"
# 允许从节点进行写功能
slave-read-only no
# 设置了密码,主节点需要进行密码认证
masterauth "password"
# 如果是Slave则需要配置Master的地址
# slaveof 10.10.10.238 6379
sentinel.conf配置内容如下:
# 检查主redis节点的地址和端口信息,1代表至少1个sentinel节点认为主节点宕机才算宕机,根据实际情况进行配置
sentinel monitor mymaster 10.10.10.44 6379 1
# 主节点失联X秒后,认为主节点不可达,默认30s
sentinel down-after-milliseconds mymaster 5000
# 主节点的密码,如有的话
sentinel auth-pass mymaster password
# sentinel后台运行
daemonize yes
# 解决哨兵之间不能通信,不能进行主结节客观下线的判断,以及failover
protected-mode no
测试
redis.conf配置文件(关键内容)如下:
# 237
daemonize yes
requirepass zhkjredis@002396
slave-read-only no
masterauth zhkjredis@002396
# 238
daemonize yes
requirepass zhkjredis@002396
slave-read-only no
masterauth zhkjredis@002396
slaveof peerServer 6379
# 104
daemonize yes
requirepass zhkjredis@002396
slave-read-only no
masterauth zhkjredis@002396
slaveof server237 6379
sentinel.conf配置文件(关键内容)如下:
# 237
sentinel monitor mymaster localServer 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster password
daemonize yes
protected-mode no
# 238
sentinel monitor mymaster peerServer 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster password
daemonize yes
protected-mode no
# 104
sentinel monitor mymaster server237 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster password
daemonize yes
protected-mode no
先启动237的redis和sentinel,然后启动238和104的组件。
通过INFO命令可以看到237为master,下面有两个slave,一个是238一个是104
# 237-Replication
role:master
connected_slaves:2
slave0:ip=10.10.10.238,port=6379,state=online,offset=17686,lag=1
slave1:ip=10.10.10.104,port=6379,state=online,offset=17408,lag=1
# 238-Replication
role:slave
master_host:peerServer
master_port:6379
master_link_status:up
# 104-Replication
role:slave
master_host:peerServer
master_port:6379
master_link_status:up
在237插入<test, test>,分别在238和104进行查询,能够查询成功,说明238和104对237同步成功。
在238插入<test238, test>,分别在237和104查询,查询失败,说明237和104并没有同步238数据,且slave能够进行数据的插入。
关闭237,此时经过选举之后,238和104中其中一个将会成为新的master,讲238重新上线,238将会成为新master的slave。
将237重新变为master,此时想指定让104成为master,需要进行以下操作:
## 先保证238和104的优先级为100,然后设置237的优先级为50
redis-cli -h 10.10.10.104 -a zhkjredis@002396 config set slave-priority 100
redis-cli -h 10.10.10.104 -a zhkjredis@002396 config rewrite
redis-cli -h 10.10.10.238 -a zhkjredis@002396 config set slave-priority 100
redis-cli -h 10.10.10.238 -a zhkjredis@002396 config rewrite
redis-cli -h 10.10.10.237 -a zhkjredis@002396 config set slave-priority 50
## 进行sentinel故障转移
redis-cli -h 10.10.10.237 -a zhkjredis@002396 -p 26379 sentinel failover mymaster
## 等待104成为master之后,讲104的优先级改回100
redis-cli -h 10.10.10.237 -a zhkjredis@002396 config set slave-priority 100
redis-cli -h 10.10.10.237 -a zhkjredis@002396 config rewrite
方案下的特殊处理
主动主备切换
保证除了指定成为新master的redis的优先级变成100,然后讲该redis的优先级设置为50,然后通过sentinel的命令进行故障转移,待转移完成之后,讲该redis的优先级改回100。
例如目前237为master,要使得104成为主,只需要保证237和238的slave优先级为100,并讲104的优先级设置为50,然后进行故障转移,等待转移完成之后将104的优先级改回100.
被动主备切换
当发生故障转移时,sentinel会选举出leader-sentinel,然后由该sentinel选择适合的redis成为新的master,原master重新上线时候,会被sentinel转换成当前master的slave。