通过 rsync sersync 实现高效的数据实时同步架构
一、Sersync的介绍
sersync主要用于服务器同步,web镜像等功能。基于boost1.43.0,inotify api,rsync command.开发。目前使用的比较多的同步解决方案是inotify-tools+rsync ,另外一个是google开源项目Openduckbill(依赖于inotify- tools),这两个都是基于脚本语言编写的。相比较上面两个项目,本项目优点是:
### Sersync 使用 c++ 编写,对 linux 系统文件产生的临时文件和重复的文件操作会进行过滤,在本文后面会提到该点。使用 rsync sersyc结合做同步的时候,会大大减少运行时所消耗的本地以及网络资源,因此在速度方面有显著提升。
### 相比 Inotify-tools 和 Openduckbill,Sersync 配置起来更为简单方便。在谷歌 Sersync 项目下载的安装包的 bin 目录下,放置了已经编译好的二进制文件,搭配 bin 目录下的xml文件可以直接部署使用。
### Sersync 采用多线程(默认10)进行同步(即可以并发同步多个不同文件),尤其是针对较大文件同步的时候,它能够保证多个服务器实时保持同步状态。
### Sersync 自带了出错处理机制。它可以通过失败队列自动对之前出错的文件进行重新同步操作。如果届时依旧失败,它会每 10 个小时对同步失败的文件再进行重新同步操作,直到文件同步为止。
### Sersync 自带有 crontab 功能,因此不需要借助系统的 crontab ,只需在 xml 配置文件中开启该功能,即可按预先的配置,每隔一段时间自动做一次整体同步操作。
### Sersync 还自带了 socket 与 refreshCDN 的协议扩展,可以满足有特殊需求的公司二次开发。(之前的版本有https扩展,目前已去除)
针对上图的设计架构,这里做几点说明,来帮助大家阅读和理解该图:
1、线程组线程是等待线程队列的守护线程,当事件队列中有事件产生的时候,线程组守护线程就会逐个唤醒同步线程。当队列中 Inotify 事件较多的时候,同步线程就会被全部唤醒一起工作。这样设计的目的是为了能够同时处理多个 Inotify 事件,从而提升服务器的并发同步能力。同步线程的最佳数量=核数 x 2 + 2。
2、那么之所以称之为线程组线程,是因为每个线程在工作的时候,会根据服务器上新写入文件的数量去建立子线程,子线程可以保证所有的文件与各个服务器同时同步。当要同步的文件较大的时候,这样的设计可以保证每个远程服务器都可以同时获得需要同步的文件。
3、服务线程的作用有三个:
- 处理同步失败的文件,将这些文件再次同步,对于再次同步失败的文件会生成 rsync_fail_log.sh 脚本,记录失败的事件。
- 每隔10个小时执行 rsync_fail_log.sh 脚本一次,同时清空脚本。
- crontab功能,可以每隔一定时间,将所有路径整体同步一次。
4、过滤队列的建立是为了过滤短时间内产生的重复的inotify信息,例如在删除文件夹的时候,inotify就会同时产生删除文件夹里的文件与删除文件夹的事件,通过过滤队列,当删除文件夹事件产生的时候,会将之前加入队列的删除文件的事件全部过滤掉,这样只产生一条删除文件夹的事件,从而减轻了同步的负担。同时对于修改文件的操作的时候,会产生临时文件的重复操作。
二、实验环境
内核版本:2.6.32-431.el6.x86_64
操作系统:centos系统采用最小化安装,系统经过了基本优化,selinux为关闭状态,iptables为无限制模式
源码包存放位置:/usr/local/src
客户端(Rsync+Sersync):这里实际生产环境下应该一个主master-rsync-sersync和一个备slave-rsync-sersync,防止单点故障
如果一个客户端宕机,备客户端会自动接管业务,采用ha-drbd架构(本实验暂时不做)
主机名:master-rsync-sersync IP:172.16.0.2
主机名:slave-rsync-sersyn IP:172.16.0.3
服务端(web1业务+rsync):IP:172.16.0.4,主机名:rsync-web-server-1
服务端(web2业务+rsync):IP:172.16.0.5,主机名:rsync-web-server-2
服务端(web3业务+rsync):IP:172.16.0.6,主机名:rsync-web-server-3
rsync版本:https://rsync.samba.org/ftp/rsync/rsync-3.1.1.tar.gz
sersync版本:https://sersync.googlecode.com/files/sersync2.5.4_64bit_binary_stable_final.tar.gz
三、rsync服务端部署
第一步:在服务端部署上rsync(3个rsync-web-server都需要操作这一步)
#最小化安装的centos6.X可能需要安装一些基础的库,不装会编译通过不了提示各种错误。
yum install gcc perl
#下载源码包,解压缩,编译安装
wget https://rsync.samba.org/ftp/rsync/rsync-3.1.1.tar.gz
tar zxvf rsync-3.1.1.tar.gz
cd cd rsync-3.1.1
./configure.sh
make && make install
第二步:在服务端上创建一个rsync配置文件(默认没有)
cat > /etc/rsyncd.conf < < EOF
#Rsync server
#created by bingoku 15:01 2014.8.19
##rsyncd.conf start##
#uid gid 表示对后面模块中的path路径拥有什么样的权限
uid = root
gid = root
#如果"use chroot"指定为true,那么rsync在传输文件以前首先chroot到path参数所指定的目录下。这样做的原因是实现额外的安全防护,但是缺 点是需要以roots权限,并且不能备份指向外部的符号连接所指向的目录文件。默认情况下chroot值为true。
use chroot = no
# 定义连接数2000
max connections = 2000
# 600秒超时
timeout = 600
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log
# 忽略错误
ignore errors
# false才能上传文件,true不能上传文件
read only = false
list = false
hosts allow = 172.16.0.2/24
hosts deny = 0.0.0.0/32
# 虚拟用户,同步时需要用这个用户
auth users = rsync_backup
# 密码文件 格式(虚拟用户名:密码)
secrets file = /etc/rsync.password
#####################################
# 模块名称
[www]
# comment注释
comment = www by bingoku 14:18 2014.8.19
##path指 模块的路径必须填写,且真实存在
path = /data0/www/www/
#####################################
[bbs]
comment = bbs by bingoku 14:18 2014.8.19
path = /data0/www/bbs/
#####################################
[blog]
comment = blog by bingoku 14:18 2014.8.19
path = /data0/www/blog/
#####################################
EOF
说明:
这是一份带有注释的配置文件。是一个多目录同步案例。可以根据自己的需求,选择,如果只同步一个目录,那就只需写一个同步模块和目录。
特别注意hosts allow和hosts deny的设定,可以做好基本的安全设置。
业务需求对被同步目录要有写入和更新的权限,因此uid和gid都用了root权限。
第三步:在服务端,创建接收同步(推送过来)的目录(必须跟配置文件中的模块指定的路径一致!)
mkdir /data0/www/{www,bbs,blog} -p
说明:
实际生产环境下,可能已经有了准备等待同步的目录,无须创建,修改模块路径就可以了。
需要在服务端都有对应的目录才可以。因此都需要执行此次创建目录,否则rsync会因为path路径问题无法启动。
第四步:在服务端上配置相关认证和权限。
#创建password密码文件且写入 虚拟用户名和密码
echo 'rsync_backup:redhat' > /etc/rsync.password
#修改密码文件赋予权限(必须操作)
chmod 600 /etc/rsync.password
说明:rsync.password 文件的权限必须修改。不然同步不了。
第五步:以守护进程的方式启动rsync服务
#默认安装路径下的rsync
/usr/local/bin/rsync --daemon
#验证是否启动成功
[root@c6mini ~]# lsof -i tcp:873
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsync 1051 root 3u IPv4 8625 0t0 TCP *:rsync (LISTEN)
rsync 1051 root 5u IPv6 8626 0t0 TCP *:rsync (LISTEN)
说明:
lsof命令一般都没有安装,需要yum install lsof 这个命令可以通过端口反查进程名称。
第六步:在服务端上面把rsync添加到开机自启动
#按标准添加一条注释,方便以后维护
echo "# rsyncd service daemon by bingoku 20140819" >>/etc/rc.local
echo "/usr/local/bin/rsync --daemon" >> /etc/rc.local
#验证下是否添加
[root@c6mini ~]# grep daemon /etc/rc.local
# rsyncd service daemon by bingoku 20140819
/usr/local/bin/rsync --daemon
说明:rsync的重启,没有命令和脚本,因此需要结束后台守护进程,然后再启动的方式重启。
#结束进程
pkill rsync
#再次启动
/usr/local/bin/rsync --daemon
特别说明:上面操作的这六个步骤都需要在,你的服务端上面执行。
四、rsync客户端上部署rsync和sersync
第一步:在客户端上安装和配置rsync的认证权限。
yum install gcc perl
cd /usr/local/src
wget https://rsync.samba.org/ftp/rsync/rsync-3.1.1.tar.gz
tar zxvf rsync-3.1.1.tar.gz
cd cd rsync-3.1.1
./configure.sh
make && make install
第二步:在客户端上,仅配置password文件且,只写入和服务端对应的密码
#仅写入和服务端虚拟用户对应的密码
echo "redhat" > /etc/rsync.password
#修改密码文件的权限(必须)
chmod 600 /etc/rsync.password
第三步:创建准备同步(推送)给服务端的目录和一些测试文件
#创建目录
mkdir /data0/www/{www,bbs,blog} -p
#创建测试文件
touch /data0/www/{www/index.html,bbs/a.jpg,blog/prr.jpg}
第四步:执行同步目录(主要是测试下推送到服务端的是否正常)
rsync -avzP /data0/www/www rsync_backup@172.16.0.4::www/ --password-file=/etc/rsync.password
rsync -avzP /data0/www/bbs rsync_backup@172.16.0.4::bbs/ --password-file=/etc/rsync.password
rsync -avzP /data0/www/blog rsync_backup@172.16.0.4::blog/ --password-file=/etc/rsync.password
说明:这里,仅记录了一个 向服务端推送的内容的命令,测试其它服务端改下主机IP就可以。同步完毕后进入服务端的相应目录查看下,文件是否存在。
确保这一步,是没人和错误提示的。
第五步:安装和规范sersync目录结构
#获取安装包:https://code.google.com/p/sersync/downloads/list
cd /usr/local/src
wget https://sersync.googlecode.com/files/sersync2.5.4_64bit_binary_stable_final.tar.gz
tar zxvf sersync2.5.4_64bit_binary_stable_final.tar.gz
cp -r GNU-Linux-x86 /usr/local/sersync
cd /usr/local/sersync/
#按标准规范sersync目录结构方便日后管理。
mkdir conf bin logs
mv confxml.xml conf
mv sersync2 bin/sersync
第六步:配置sersync
#备份一份默认的conxml.xml作为备份
cp conf/confxml.xml conf/confxml.xml.bak.$(date +%F)
#mv一个conxml.xml为 conxml_www.xml
cd conf
mv confxml.xml confxml_www.xml
#修改conxml_www.xml配置文件 达到能同步且高效的作用.
#
#修改24-28行的内容,原内容为:
#
#定义本地要同步的目录
<localpathwatch ="/opt/tongbu"> #需要监视的目录
#同步到哪个机器,,同步到机器的哪个模块
<remoteip ="127.0.0.1" name="tongbu1"></remoteip> #需要同步到的服务器和路径
<!--<remoteip="192.168.8.39" name="tongbu"/>-->
<!--<remoteip="192.168.8.40" name="tongbu"/>-->
#
#修改后的内容:
#
</localpathwatch><localpathwatch ="/data0/www/www">
<remoteip ="172.16.0.4" name="www"></remoteip>
<remoteip ="172.16.0.5" name="www"></remoteip>
<remoteip ="172.16.0.6" name="www"></remoteip>
#
#修改31-34行,和认证相关部分,原内容为:
#
<commonparamsparams ="-artuz"></commonparamsparams> #同步内容
<auth start="false"users="root" passwordfile="/etc/rsync.pas"></auth>
<userdefinedportstart ="false" port="874"></userdefinedportstart><!-- port=874 -->
<timeoutstart ="false" time="100"></timeoutstart><!-- timeout=100 --> #是否超时100分钟后重新同步
<sshstart ="false"></sshstart>
#
#修改后的内容为:
#
<commonparamsparams ="-aruz"></commonparamsparams> #同步内容
<auth start="true"users="rsync_bak"passwordfile="/etc/rsync.password"></auth>
<userdefinedportstart ="false" port="874"></userdefinedportstart><!-- port=874 -->
<timeoutstart ="true" time="100"></timeoutstart><!-- timeout=100 --> #是否超时100分钟后重新同步
<sshstart ="false"/
说明:上面修改的这两部分实际上就是跟我们前面测试能否推送内容的那些命令是一样的意思。
#还需要修改一下日志记录的路径
#
#原版内容为:
#
<failLogpath="/tmp/rsync_fail_log.sh"timeToExecute="60"></sshstart><!--default every 60mins execute once-->
#
#修改后的内容为:
#
<faillog path="/usr/local/sersync/logs/rsync_fail_log.sh "timeToExecute="60"></faillog><!--default every 60mins execute once-->
说明:当同步失败后,日志记录到/usr/local/sersync/logs/rsync_fail_log.sh,并且每60分钟对失败的log进行重新同步。时间可以根据业务量进行缩短。
一份完整的sersync配置文件:
< ?xml version="1.0" encoding="ISO-8859-1"?>
<head version="2.5">
<host hostip="localhost" port="8008"></host>
<debug start="false"></debug>
<filesystem xfs="false"></filesystem>
<filter start="false">
<exclude expression="(.*)\.svn"></exclude>
<exclude expression="(.*)\.gz"></exclude>
<exclude expression="^info/*"></exclude>
<exclude expression="^static/*"></exclude>
</filter>
<inotify>
<delete start="true"></delete>
<createfolder start="true"></createfolder>
<createfile start="false"></createfile>
<closewrite start="true"></closewrite>
<movefrom start="true"></movefrom>
<moveto start="true"></moveto>
<attrib start="false"></attrib>
<modify start="false"></modify>
</inotify>
<sersync>
<localpath watch="/data0/www/www">
<remote ip="172.16.0.4" name="www"></remote>
<remote ip="172.16.0.5" name="www"></remote>
<remote ip="172.16.0.6" name="www"></remote>
</localpath>
<rsync>
<commonparams params="-artuz"></commonparams>
<auth start="true" users="rsync_backup" passwordfile="/etc/rsync.password"></auth>
<userdefinedport start="false" port="874"></userdefinedport><!-- port=874 -->
<timeout start="false" time="100"></timeout><!-- timeout=100 -->
<ssh start="false"></ssh>
</rsync>
<faillog path="/usr/local/sersync/logs/www_rsync_fail_log.sh" timeToExecute="60"></faillog><!--default every 60mins execute once-->
<crontab start="false" schedule="600"><!--600mins-->
<crontabfilter start="false">
<exclude expression="*.php"></exclude>
<exclude expression="info/*"></exclude>
</crontabfilter>
</crontab>
<plugin start="false" name="command"></plugin>
</sersync>
<plugin name="command">
<param prefix="/bin/sh" suffix="" ignoreError="true"/> <!--prefix /opt/tongbu/mmm.sh suffix-->
<filter start="false">
<include expression="(.*)\.php"></include>
<include expression="(.*)\.sh"></include>
</filter>
</plugin>
<plugin name="socket">
<localpath watch="/opt/tongbu">
<deshost ip="192.168.138.20" port="8009"></deshost>
</localpath>
</plugin>
<plugin name="refreshCDN">
<localpath watch="/data0/htdocs/cms.xoyo.com/site/">
<cdninfo domainname="ccms.chinacache.com" port="80" username="xxxx" passwd="xxxx"></cdninfo>
<sendurl base="https://pic.xoyo.com/cms"></sendurl>
<regexurl regex="false" match="cms.xoyo.com/site([/a-zA-Z0-9]*).xoyo.com/images"></regexurl>
</localpath>
</plugin>
</head>
说明:这里也仅仅配置了一个模块[www]的sersync的自动同步。sersync的多实例和nginx,Apache类似,需要多个不同的配置文件,来做相应的修改就可以了。例如:
#因为我有3个模块所以需要3份配置文件。一一做相应的修改就可以
cp /usr/local/sersync/conf/confxml.xml /usr/local/sersync/conf/www_conxml.xml
cp /usr/local/sersync/conf/confxml.xml /usr/local/sersync/conf/bbs_conxml.xml
cp /usr/local/sersync/conf/confxml.xml /usr/local/sersync/conf/blog_conxml.xml
第七步:启动sersync!
#直接终端运行,配置文件就可以
/usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/www_conxml.xml
/usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/bbs_conxml.xml
/usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/blog_conxml.xml
#会显示类似的结果,只要没有error一切都OK
[root@c6mini www]# /usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/confxml_www.xml
set the system param
execute:echo 50000000 > /proc/sys/fs/inotify/max_user_watches
execute:echo 327679 > /proc/sys/fs/inotify/max_queued_events
parse the command param
option: -r rsync all the local files to the remote servers before the sersync work
option: -d run as a daemon
option: -o config xml name: /usr/local/sersync/conf/confxml_www.xml
daemon thread num: 10
parse xml config file
host ip : localhost host port: 8008
daemon start,sersync run behind the console
use rsync password-file :
user is rsync_backup
passwordfile is /etc/rsync.password
config xml parse success
please set /etc/rsyncd.conf max connections=0 Manually
sersync working thread 12 = 1(primary thread) + 1(fail retry thread) + 10(daemon sub threads)
Max threads numbers is: 22 = 12(Thread pool nums) + 10(Sub threads)
please according your cpu ,use -n param to adjust the cpu rate
------------------------------------------
rsync the directory recursivly to the remote servers once
working please wait...
execute command: cd /data0/www/www && rsync -artuz -R --delete ./ --timeout=100 rsync_backup@172.16.0.2::www --password-file=/etc/rsync.password >/dev/null 2>&1
run the sersync:
watch path is: /data0/www/www
#验证启动的进程
[root@c6mini ~]# ps -ef |grep sersync
root 1096 1 0 14:05 ? 00:00:00 /usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/confxml_bbs.xml
root 1114 1 0 14:05 ? 00:00:00 /usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/confxml_www.xml
root 1132 1 0 14:05 ? 00:00:00 /usr/local/sersync/bin/sersync -r -d -o /usr/local/sersync/conf/confxml_blog.xml
root 1211 1195 0 14:05 pts/0 00:00:00 grep sersync
第八步:配置开机自启动
#备份一下rc.local文件
cp /etc/rc.local /etc/rc.local.bak._$(date +%F)
#末尾追加sersync命令
cat >>/etc/rc.local< < 'EOF'
# resync data to 172.16.0.4,172.16.0.5,172.16.0.6
/usr/local/sersync/bin/sersync -d -o /usr/local/sersync/conf/www_conxml.xml
/usr/local/sersync/bin/sersync -d -o /usr/local/sersync/conf/bbs_conxml.xml
/usr/local/sersync/bin/sersync -d -o /usr/local/sersync/conf/blog_conxml.xml
EOF
第九步:测试sersync是否正常!
#测试方法很多,模拟人工的方式,直接在客户端上面创建文件,在服务端查看。
#这里测试使用shell批量创建大量文件来测试效果。
#
#这是批量复制一张图片,来测试性能。
for i in {1..10000};do cp /root/cp/10k.jpg /data0/www/www/10k_`echo $(date)$RANDOM|md5sum |cut -c 1-8`.jpg;done
for i in {1..10000};do cp /root/cp/20k.jpg /data0/www/bbs/10k_`echo $(date)$RANDOM|md5sum |cut -c 1-8`.jpg;done
for i in {1..10000};do cp /root/cp/50k.jpg /data0/www/blog/10k_`echo $(date)$RANDOM|md5sum |cut -c 1-8`.jpg;done
#可以写进一个.sh脚本文件里面,一起执行。
可以通过 ps -ef |grep rsync|wc -l 来查看进程数,多次查看发现这个进程是变化的。
root@c6mini www]# ps -ef |grep rsync|wc -l
20
[root@c6mini www]# ps -ef |grep rsync|wc -l
10
[root@c6mini www]# ps -ef |grep rsync|wc -l
14
[root@c6mini www]# ps -ef |grep rsync|wc -l
18
[root@c6mini www]# ps -ef |grep rsync|wc -l
说明:实际生产环境下,服务器的配置都很高,硬盘的性能都是采用硬件阵列技术,读写的性能会高许多,因此读写就很快,进程数也就多。当然这个也跟数据量有一定的关系。主要还是看机器的性能。一般来说以 DELL R710 服务器为主,在千兆网卡情况下,每秒钟同步 10-100k 的文件能达到同步 40 到 50 个。
关于sersync的参数说明:
/usr/local/sersync/bin/sersync -help
参数-d:启用守护进程模式
参数-r:在监控前,将监控目录与远程主机用rsync命令推送一遍
参数-n: 指定开启守护线程的数量,默认为10个
参数-o:指定配置文件,默认使用confxml.xml文件
参数-m:单独启用其他模块,使用 -m refreshCDN 开启刷新CDN模块
参数-m:单独启用其他模块,使用 -m socket 开启socket模块
参数-m:单独启用其他模块,使用 -m https 开启https模块
不加-m参数,则默认执行同步程序
关于sersync的插件使用
当plugin标签设置为true时候,在同步文件或路径到远程服务器之后,会调用插件。通过name参数指定需要执行的插件。目前支持的有command、refreshCDN、socket、https四种插件。其中,https插件目前由于兼容性原因已经去除,以后会重新加入。
#只调用command插件
/usr/local/sersync/bin/sersync -d -m command
#只调用refreshCDN插件:插件refreshCDN的相关配置refreshCDN 用来在同步过程中将文件发送到目地服务器后,刷新cdn接口。如果不想使用,则将start属性设为false即可。该模块根据chinaCDN的协议,进行设计,当有文件产生的时候,就向cdn解耦发送需要刷新的路径为止。其中localpath watch=“/data0/htdocs/cms.xoyo.com/site/”是需要监控的目录。cdinfo标签指定了cdn接口的域名,端口号,以及用户名与密码。sendurl 标签是需要刷新的url的前缀。regexurl 标签中,regex属性为true时候,使用match属性的正则语句匹配inotify返回的路径信息,并将正则匹配到的部分作为url一部分下面配置文件自带的意思为,如果产生文件事件为:/data0/htdoc/cms.xoyo.com/site/jx3.xoyo.com/image/a/123.txt经过上面的match正则匹配后,最后刷新的路径是:https://pic.xoyo.com/cms/a/123.txt如果regex属性为false,最后刷新的路径就是:https://pic.xoyo.com/cms/jx3.xoyo.com/images/a/123.txt
/usr/local/sersync/bin/sersync -d -m refreshCDN
#只调用socket插件:socket插件,开启该模块,则向指定ip与端口发送inotify所产生的文件路径信息
/usr/local/sersync/bin/sersync -d -m socket
#只调用https插件
/usr/local/sersync/bin/sersync -d -m https</localpathwatch></localpathwatch>
转自:https://blog.csdn.net/punk_lover/article/details/76854138