Rsync客户端卡死的问题查询
简介
某备份系统大量使用rsync来传输文件,但是偶尔会出现rsync客户端在上传数据的时候长时间卡死,本文记录了解决问题的步骤。
本文只涉及rsync客户端中IO相关逻辑,关于rsync的发送算法并不涉及,服务端逻辑略有提到。
故障现象
rsync客户端一直驻留内存,strace跟踪rsync客户端进程发现:
# strace -p 22819
Process 22819 attached - interrupt to quit
select(4, [3], [], NULL, {57, 106010}) = 0 (Timeout)
select(4, [3], [], NULL, {60, 0}) = 0 (Timeout)
select(4, [3], [], NULL, {60, 0}) = 0 (Timeout)
select(4, [3], [], NULL, {60, 0}) = 0 (Timeout)
select(4, [3], [], NULL, {60, 0}) = 0 (Timeout)
select(4, [3], [], NULL, {60, 0}) = 0 (Timeout)
select(4, [3], [], NULL, {60, 0}) = 0 (Timeout)
故障原因查询
- 因为rsync在编译时没有把代码信息编译进去(也就是没有加上
-g选项),所以gdb也无法跟踪具体的调用堆栈。 - 但是从上面的跟踪可以看出,进程一直在等待fd=3的读取事件,每次都是超时(默认60秒)。
- ok,先查查这个fd=3是什么:
# ll /proc/22819/fd
total 0
lr-x------ 1 root root 64 Jan 4 19:24 0 -> /dev/null
l-wx------ 1 root root 64 Jan 4 19:24 1 -> pipe:[1247832664]
l-wx------ 1 root root 64 Jan 4 19:24 2 -> pipe:[1247832664]
lrwx------ 1 root root 64 Jan 4 19:24 3 -> socket:[1247890095]
- 可见fd=3是一个socket,查看这个socket的源地址和目标地址:
# grep 1247890095 /proc/net/tcp
13: 3EA8010A:4CC5 3D21010A:22A9 01 00000000:00000000 00:00000000 00000000 0 0 1247890095 1 ffff8808772ae940 23 3 24 3 7
- 源地址【3EA8010A:4CC5】,转换成十进制就是【10.1.168.62:19653】,也就是本机地址;目标地址【3D21010A:22A9】,转换成十进制就是【10.1.33.61:8873】。至于如何转换不再赘述,参考UNP中关于数据的主机顺序和网络顺序的论述。
- 连接目标机器10.1.33.61,发现这台机器上根本没有与【10.1.168.62:19653】的连接,也就是服务端连接已经关闭。
- 因此,上述故障原因已经查明:服务端关闭连接,但是客户端仍在重试等待来自服务端的读取信息。默认的60秒只是select的超时时间,但是如果没有指定连接的超时时间,那么客户端会一直死等(没有keep alive等等,根据rsync 3.0.6代码)。
Talk is cheap,show me your code
基于极客精神,我有兴趣查看源代码,看看rsync为什么会犯这种错误。代码为rsync 3.0.6,使用工具查看,可以得到调用关系(图有点大,点开看)。

客户端调用路线(大概):main->start_client->send_files->read_ndx_and_attrs->readfd_unbuffered->read_loop->read_timeout,最后跟踪到
read_timeout函数,代码如下所示(中文注释是我添加的):
/**
* Read from a socket with I/O timeout. return the number of bytes
* read. If no bytes can be read then exit, never return a number <= 0.
*
//对于读取失败的问题,这里列为TODO
* TODO: If the remote shell connection fails, then current versions
* actually report an "unexpected EOF" error here. Since it's a
* fairly common mistake to try to use rsh when ssh is required, we
* should trap that: if we fail to read any data at all, we should
* give a better explanation. We can tell whether the connection has
* started by looking e.g. at whether the remote version is known yet.
*/
static int read_timeout(int fd, char *buf, size_t len)
{
int n, cnt = 0;
io_flush(FULL_FLUSH);
while (cnt == 0) {
/* until we manage to read *something* */
fd_set r_fds, w_fds;
struct timeval tv;
int maxfd = fd;
int count;
FD_ZERO(&r_fds);
FD_ZERO(&w_fds);
FD_SET(fd, &r_fds); //要从fd读取,把它加到读取集合中
if (io_filesfrom_f_out >= 0) {
int new_fd;
if (ff_buf.len == 0) {
if (io_filesfrom_f_in >= 0) {
FD_SET(io_filesfrom_f_in, &r_fds);
new_fd = io_filesfrom_f_in;
} else {
io_filesfrom_f_out = -1;
new_fd = -1;
}
} else {
FD_SET(io_filesfrom_f_out, &w_fds);
new_fd = io_filesfrom_f_out;
}
if (new_fd > maxfd)
maxfd = new_fd;
}
tv.tv_sec = select_timeout; //设置超时,如果没有指定,默认60秒
tv.tv_usec = 0;
errno = 0;
count = select(maxfd + 1, &r_fds, &w_fds, NULL, &tv); //select调用
//如果超时或者服务端关闭socket,都会返回count<0
if (count <= 0) {
if (errno == EBADF) {
defer_forwarding_messages = 0;
exit_cleanup(RERR_SOCKETIO);
}
check_timeout(); //处理超时,注意这两句,如果超时,需要在check_timeout()中退出,否则会一直循环
continue;
}
//下面的代码忽略
check_timeout函数(关键):
static void check_timeout(void)
{
time_t t;
if (!io_timeout || ignore_timeout) //注意,客户端没有--timeout选项,io_timeout会默认为0,也就是直接返回
return;
if (!last_io_in) {
last_io_in = time(NULL);
return;
}
t = time(NULL);
if (t - last_io_in >= io_timeout) {
if (!am_server && !am_daemon) {
rprintf(FERROR, "io timeout after %d seconds -- exiting\n",
(int)(t-last_io_in));
}
exit_cleanup(RERR_TIMEOUT);
}
}
- 总结一下:
- 客户端select默认60秒超时,超时之后检查连接是否超时,如果超时则调用
exit_cleanup退出; - 如果客户端没有指定
--timeout,那么io_timeout=0,程序会一直在select()中超时;
- 客户端select默认60秒超时,超时之后检查连接是否超时,如果超时则调用
最新版本是否解决了这个问题?rsync-3.1.3
rsync-3.1.3版本的代码调用堆栈如下:

从这里看出,检查超时的时候,新版的rsync会尝试发送一个keep_alive到服务端,如果读写成功,表示服务端还存活,则
perform_io()函数会更新时间戳,那么在check_timeout()的后续判断中就不会被判断为超时。
总结以及解决办法
- 在旧版本rsync中,当客户端正在读取服务端的信息,而此时服务端因为某种原因而断开连接(如服务器挂了,进程被kill了),客户端会出现循环等待,造成卡死的现象,这是本次要查的问题。
- 原因出现在,旧版rsync没有处理好上述问题,只是单纯以超时判断,而不去试探服务端是否存活,或者链接是否有效。
- 新版的解决办法:select超时之后尝试使用keep alive报文去写socket,尝试socket是否读写成功,如果成功,则更新socket的时间戳,相当于为这个socket加血续命。
- 可以考虑的解决办法:
- 更新rsync到新版,尤其是客户端;
- 客户端调用时加上
--timeout选项指定超时;
Rsync客户端卡死的问题查询的更多相关文章
- Swing EDT引起的客户端卡死
最近调试程序时发现,点击某个界面时会出现卡死的情况,出现的频率还是比较频繁的. 再次出现卡死的情况后,利用jvisualvm查看线程的运行情况,dump操作之后发现线程间出现了死锁: Found on ...
- window安装rsync客户端和服务端
原文地址: https://www.cnblogs.com/janas/p/3321087.html 下载地址: https://linux.linuxidc.com/index.php?folder ...
- Linux使用rsync客户端与服务端同步目录进行备份
一.服务端设置 1. 修改 server 端配置 # vi /etc/rsyncd.conf 修改: uid = nobody # 该选项指定当该模块传输文件时守护进程应该具有的uid.默认值为&qu ...
- windows 上rsync客户端使用方法
1.1 获取 windows上实现rsync的软件(cwRsync) cwRsync是Windows 客户端GUI的一个包含Rsync的包装.您可以使用cwRsync快速远程文件备份和同步. 1.1. ...
- Hbase 学习(四) hbase客户端设置缓存优化查询
我们在用hbase的api对hbase进行scan操作的时候,可以设置caching和batch来提交查询效率,那它们之间的关系是啥样的呢,我们又应该如何去设置? 首先是我们的客户端代码. 当cach ...
- 使用MySQL客户端登录Ensemble数据库查询相关信息
Ensemble公共MySQL数据库 对于大量数据和更详细的分析,Ensemble的MySQL服务器ensembldb.ensembl.org,useastdb.ensembl.org或asiadb. ...
- rsync客户端一键安装rsync脚本(源码)
客户端 read -np "请输入源码rsync的URL 地址 包名(以空格为分隔符,别带/):" URL DZ BM yum remove -y rsync &>& ...
- Rsync 客户端配置
安装rsync服务yum -y install rsync 创建密码文件rsync.passwordvi /etc/rsync.password只存储密码即可,无需用户名.密码为Rsync服务器端的/ ...
- MongoDB的客户端管理工具--nosqlbooster 查询工具使用
连接我的MongoDB 看到这样 打开db1数据库里面user集合,看到user集合里面的数据,他会自带查询语句 看这里以tree方式显示 可以以table方式显示 还可以json方式显示 按照自己的 ...
随机推荐
- Nginx 动静分离与负载均衡的实现
一.前提 企业中,随着用户的增长,数据量也几乎成几何增长,数据越来越大,随之也就出现了各种应用的瓶颈问题. 问题出现了,我们就得想办法解决,一般网站环境,均会使用LAMP或者LNMP,而我们对于网站环 ...
- 侯哥的Python分享
侯哥语录 我曾经是一个职业教育者,现在是一个自由开发者.我希望我的分享可以和更多人一起进步.分享一段我喜欢的话给大家:"我所理解的自由不是想干什么就干什么,而是想不干什么就不干什么.当你还没 ...
- 文本编辑器激活系列(一):Sublime 安装、激活、汉化教程
如您激活出现问题,请点击这里加入:软件激活问题解决群 前言 推荐几款文本编辑器: Sublime:内嵌python解释器.大量插件 EditPlus:语法着色.内嵌浏览器 Notepad++:所见即所 ...
- .NET Core 使用 HttpClient SSL 请求出错的解决办法
问题 使用 HTTP Client 请求 HTTPS 的 API 时出现 The certificate cannot be verified up to a trusted certificatio ...
- 基于 Redis 的分布式锁
前言 分布式锁在分布式应用中应用广泛,想要搞懂一个新事物首先得了解它的由来,这样才能更加的理解甚至可以举一反三. 首先谈到分布式锁自然也就联想到分布式应用. 在我们将应用拆分为分布式应用之前的单机系统 ...
- Unity资源 ----加载最好需要做哪些事
先上图解 一.基本关键词 1)AssetBundle:一种保存“一个或多个资源的转变为某种利于传输等的特殊格式(二进制之类)”的文件.(我这边是使用Unity制作手游的角度来说明) 简称AB. 2)对 ...
- Python模块查找路径
在编写Python程序时候命名在IDE中运行正常可是到了服务器上就出现各种问题,经常发生的就是如下的错误: ImportError: No module named 'hello' 这个错误我相信大家 ...
- Redis【入门】就这一篇!
Redis 概述 在我们日常的Java Web开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一 ...
- Docker Compose 原理
Docker 的优势非常明显,尤其是对于开发者来说,它提供了一种全新的软件发布机制.也就是说使用 docker 镜像作为软件产品的载体,使用 docker 容器提供独立的软件运行上下文环境,使用 do ...
- 权限管理系统之集成Shiro实现登录、url和页面按钮的访问控制
用户权限管理一般是对用户页面.按钮的访问权限管理.Shiro框架是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理,对于Shiro的介绍这里就不多说.本篇博客主要是了解Shiro的 ...