从网络连接读取时进程无限期挂起

Mic*_*ler 7 linux python svn mac-osx

对以下内容的更新:

我在不同数据中心的 Debian 虚拟机上的不相关脚本上遇到了类似的问题。

这看起来很像这里描述的问题(就像问这个问题的人一样,我没有在服务器前配置代理)。

与以下描述的主要区别在于,当我附加到挂起的进程时,我看到的是对recvfrom而不是的调用read

$ strace -p 17527
Process 17527 attached - interrupt to quit
recvfrom(3, 
Run Code Online (Sandbox Code Playgroud)

然而,Python 并没有任何被代理的印象:

>>> import os; print os.getenv("HTTP_PROXY"), os.getenv("http_proxy")
None, None
Run Code Online (Sandbox Code Playgroud)

所以我还是很难过。可悲的是,链接的问题也没有最终答案。

(我也想知道这个问题是否相关,但 S3 似乎不太可能不尊重Connection: close标题。)


我有几个 Debian(Wheezy、x86_64)服务器都表现出以下行为:

所有服务器都有一组 cron 作业,其中包括从 S3 中提取数据。这些通常运行良好,但偶尔会ps aux显示一些小时或几天前开始的作业仍在运行,并且没有完全完成。

通过strace -p <pid>显示检查它们,在所有情况下,该进程都挂在读取命令上。比如我刚才查的一个进程的输出是:

$ strace -p 12089
Process 12089 attached - interrupt to quit
read(5, 
Run Code Online (Sandbox Code Playgroud)

检查打开的文件描述符给了我这个:

$ sudo lsof -i | grep 12089
python  12089    user    5u  IPv4 809917771      0t0  TCP my.server.net:35427->185-201.amazon.com:https (ESTABLISHED)
Run Code Online (Sandbox Code Playgroud)

起初我认为这只是由于在 Python 脚本中没有设置读取超时,但事实并非如此,原因如下:

  1. 当在我们的 OS X 机器(所有 10.5、i386)上使用相同的代码运行相同的作业时,不会发生这种情况。
  2. 脚本的一种变体设置超时(60秒,使用socket.setdefaulttimeout-这是在Python 2.7,但代码基座具有为2.5兼容)从昨天已挂起。
  3. 另一个不是 Python 的进程似乎偶尔会表现出类似的行为。在这种情况下,一个 Python 脚本正在执行一个svn up --non-interactive进程(使用subprocess.Popen, 是值得的)。

那个SVN进程的情况是相似的——

Python 正在等待 SVN:

$ strace -p 28034
Process 28034 attached - interrupt to quit   
wait4(28127, 
Run Code Online (Sandbox Code Playgroud)

并且 SVN 正在等待read调用完成:

$ strace -p 28127
Process 28127 attached - interrupt to quit
read(6, 
Run Code Online (Sandbox Code Playgroud)

该读取指向另一个外部主机:

$ sudo lsof -i | grep 28127
svn     28127    user    3u  IPv4 701186417      0t0  TCP my.server.net:49299->sparrow.telecommunity.com:svn (ESTABLISHED)
svn     28127    user    6u  IPv4 701186439      0t0  TCP my.server.net:49309->sparrow.telecommunity.com:svn (ESTABLISHED)
Run Code Online (Sandbox Code Playgroud)

(似乎在正在更新的目录上svn:externals设置了一个属性ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup;根据他们的网站,我认为这是重定向到 Telecommunity.com)

其他可能相关的要点:

  • Mac 上的 Python 环境是 2.5。在 Debian 机器上,它是 2.7。
  • 我不太精通 SVN,我不知道它挂起的原因是否从根本上是一样的。我也不完全确定其含义svn:externals是什么;这是在我之前建立的。
  • Python 脚本本身正在从 Amazon S3 中检索大量(在某些情况下约为 10MB)数据块,这有一种缓慢的趋势(我看到下载时间长达三分钟,与服务器需要很长时间——即使在不同的数据中心——相互通信)。同样,我们的一些 SVN 存储库相当大。所以这基本上是说其中一些操作无论如何都是长时间运行,但是在某些情况下它们似乎也会挂起数小时或数天。
  • 在一台服务器上,今天早上OOM杀手拿出了MySQL。仔细观察,内存使用率为 90%,交换使用率为 100%(根据 Monit 报告);杀死大量积压的 Python 作业后,这些统计数据分别减少到 60% 和 40%。这给我的印象是至少有一些(如果不是全部)数据正在下载/读取(并在进程挂起时保存在内存中)。
  • 这些 cron 作业从 S3 请求资源列表,并相应地更新 MySQL 表列表。每个作业都以相同的列表开始,因此将尝试请求相同的资源并更新相同的表。
  • 我能够从一个挂起的进程中捕获一些流量;这对我来说有点不可思议,但我想知道它是否表明连接处于活动状态并且可以正常工作,只是非常非常慢?我已经提供了它作为要点,以避免混乱(我应该注意,这大约需要两个小时的捕获时间):https : //gist.github.com/petronius/286484766ad8de4fe20b 我认为这是一个红鲱鱼。该端口上有活动,但它与到 S3 的连接不同——它只是其他随机服务器活动。
  • 我试图在另一个数据中心的机器上重新创建这个问题(一个运行相同版本的 Debian 和相同系统设置的虚拟机),但没有成功(我认为这个问题可能与这个有关,但是遇到这些问题的盒子不是虚拟机,并且根据ifconfig)没有丢包。我想这表明存在网络配置问题,但我不确定从哪里开始。

所以我想我的问题是:

  • 我可以在系统级别解决这个问题,还是每个进程都有问题?
  • 对于 OS X 和 Linux 如何处理read我需要知道的调用以避免无限挂起的进程,有什么根本不同吗?

Mat*_*Ife 3

我可以在系统级别修复此问题,还是每个单独的进程都出了问题?

很难说,因为尚不清楚协议级别发生了什么。基本上,read(2)将无限期地阻止提供:-

  • TCP 连接保持打开状态。
  • 您预计至少有 1 个字节的数据到达。
  • 发送者尚未准备好向您发送数据。

现在,该过程可能出现问题,例如另一端在发送更多数据之前首先期待您的响应,或者另一端先前的响应预计 SVN 在请求更多数据之前执行其他操作。例如,假设返回错误响应,这将迫使客户端重新发送一些信息。

您无法优雅地解决此问题,因为您无法根据所掌握的信息确定该数据的发送者希望您做什么。但是,有一些可能的方法可以避免该问题并报告该问题。

  • 不要使用wait简单的阻塞模式,而是wait在父进程中运行和配置警报。现在,当该进程未能在固定时间内完成时,您可以终止它并报告发生的情况。执行此操作的一种廉价方法是更改​​ subprocess.Popen 来调用该timeout命令。
  • 修改读取,使其设置读取超时套接字选项。您可以通过更改代码来实现此目的,或者 - 使用插入器覆盖默认socket系统调用以向接收器添加超时。这两件事都不是小事。这可能会导致svn出现意想不到的行为。

OS X 和 Linux 处理读取调用的方式是否存在根本不同,我需要知道这些不同之处以避免无限挂起的进程?

我不知道这个问题的答案,但是如果两者的行为都正确,那么它们的行为方式应该相同。如果您尝试从尚未准备好向您发送数据的套接字读取数据,则无限期地阻塞流是预期的行为。

总的来说,我认为你最好的攻击选择是期望你的svn命令在特定的时间段内完成。如果它没有杀死它并报告你这样做了。