多个Python进程变慢

时间:2011-10-01 09:01:16

标签: python performance http unix task

我有一个python脚本,它会向各个域发出大量的HTTP和urllib请求。

我们有大量的域名来处理,需要尽快完成。 由于HTTP请求很慢(即它们可能超出域上没有网站),我会在任何时候运行一些脚本,从数据库中的域列表中提取它们。

我看到的问题是在一段时间内(几小时到24小时)脚本都开始变慢,ps -al显示它们正在睡觉。

服务器非常强大(8核,72GB ram,6TB Raid 6等80MB 2:1连接)并且永远不会超出,即Free -m显示

-/+ buffers/cache:      61157      11337
Swap:         4510        195       4315

热门显示80-90%闲置

sar -d显示平均5.3%的工具

更有趣的是,iptraf以大约50-60MB / s的速度开始,大约4小时后以8-10MB / s的速度结束。

我目前在每台服务器(2台服务器)上运行大约500个版本的脚本,它们都显示相同的问题。

ps -al表明大多数python脚本都在睡觉,我不明白为什么 例如:

0 S 0 28668  2987  0  80   0 - 71003 sk_wai pts/2 00:00:03 python
0 S 0 28669  2987  0  80   0 - 71619 inet_s pts/2 00:00:31 python
0 S 0 28670  2987  0  80   0 - 70947 sk_wai pts/2 00:00:07 python
0 S 0 28671  2987  0  80   0 - 71609 poll_s pts/2 00:00:29 python
0 S 0 28672  2987  0  80   0 - 71944 poll_s pts/2 00:00:31 python
0 S 0 28673  2987  0  80   0 - 71606 poll_s pts/2 00:00:26 python
0 S 0 28674  2987  0  80   0 - 71425 poll_s pts/2 00:00:20 python
0 S 0 28675  2987  0  80   0 - 70964 sk_wai pts/2 00:00:01 python
0 S 0 28676  2987  0  80   0 - 71205 inet_s pts/2 00:00:19 python
0 S 0 28677  2987  0  80   0 - 71610 inet_s pts/2 00:00:21 python
0 S 0 28678  2987  0  80   0 - 71491 inet_s pts/2 00:00:22 python

脚本中没有睡眠状态被执行所以我无法理解为什么ps -al显示大多数睡眠状态以及为什么它们应该越来越慢,在CPU,内存,磁盘访问时随着时间的推移产生更少的IP请求和带宽都很丰富。

如果有人可以提供帮助,我将非常感激。

编辑:

代码很大,因为我通过它使用异常来捕获有关域的诊断,即我无法连接的原因。如果需要,可以在某处发布代码,但是通过HTTPLib和URLLib进行的基本调用直接来自python示例。

更多信息:

两个

quota -u mysql quota -u root

什么都没回来

nlimit -n以1024返回 有更改limit.conf以允许mysql允许16000个软连接和硬连接,并且到目前为止能够运行超过2000个脚本但仍然是问题。

一些进展

好的,所以我已经改变了用户的所有限制,确保所有套接字都关闭(它们没有),虽然情况比较好,但我仍然慢下来虽然没有那么糟糕。

有趣的是我也注意到了一些内存泄漏 - 脚本运行的时间越长,内存越多,但是我不确定是什么导致这种情况。 我将输出数据存储在一个字符串中,然后在每次迭代后将其打印到终端,我也在最后清除了字符串,但是不断增加的内存可以降低到存储所有输出的终端吗?

编辑:似乎没有 - 跑出30个脚本而没有输出到终端但仍然是相同的泄漏。 我没有使用任何聪明的东西(只是字符串,HTTPlib和URLLib) - 想知道python mysql连接器是否有任何问题......?

4 个答案:

答案 0 :(得分:7)

检查ulimitquota框以及运行脚本的用户。 /etc/security/limits.conf还可能包含您可能想要修改的资源限制。

ulimit -n将显示允许的最大打开文件描述符数。

  • 所有打开的插座都可能超出此范围吗?
  • 脚本在完成后会关闭每个套接字吗?

您还可以使用ls -l /proc/[PID]/fd/检查fd,其中[PID]是其中一个脚本的进程ID。

需要查看一些代码来说明实际情况。


修改导入评论及其他疑难解答):

您能否显示打开关闭连接的代码?
当运行一些脚本进程时,它们是否也开始运行一段时间后闲着?或者只有当有几百个+一次运行时才发生这种情况?是否有一个父进程启动所有这些脚本?

如果您使用s = urllib2.urlopen(someURL),请确保完成s.close()。 Python可以经常关闭你的东西(比如你正在做x = urllib2.urlopen(someURL).read()),但如果你被告知(例如分配),它会留给变量为.urlopen()的返回值。仔细检查urllib调用的打开和关闭(或所有 I / O代码是否安全)。如果每个脚本被设计为一次只有一个打开的套接字,并且/proc/PID/fd每个脚本进程显示多个活动/打开套接字,那么肯定会有一个代码问题需要修复。

显示ulimit -n

1024正在为 mysql 用户提供 socket / fd 限制 ,您可以使用ulimit -S -n [LIMIT_#]进行更改,但请先查看此文章:
Changing process.max-file-descriptor using 'ulimit -n' can cause MySQL to change table_open_cache value

您可能需要在之后退出并重新登录。和/或将其添加到/etc/bashrc(如果您更改source /etc/bashrc并且不想退出/进入,请不要忘记bashrc

磁盘空间是我发现的另一件事(困难的方式)会导致非常奇怪的问题。我的进程就像它们正在运行(没有被僵尸)但没有做到预期的,因为它们在没有磁盘空间的分区上有一个日志文件的打开句柄。

netstat -anpTee | grep -i mysql还将显示这些套接字是否已连接/建立/等待关闭/等待超时/等等。

watch -n 0.1 'netstat -anpTee | grep -i mysql' 在一个漂亮的表输出中看到实时中的套接字打开/关闭/更改状态/ etc(可能需要export GREP_OPTIONS=首先,如果你将它设置为--color=always)。

lsof -u mysqllsof -U也会显示开放的FD(输出非常详细)。


import urllib2
import socket

socket.settimeout(15) 
# or settimeout(0) for non-blocking:
#In non-blocking mode (blocking is the default), if a recv() call 
# doesn’t find any data, or if a send() call can’t
# immediately dispose of the data,
# a error exception is raised.

#......

try:
    s = urllib2.urlopen(some_url)
    # do stuff with s like s.read(), s.headers, etc..
except (HTTPError, etcError):
    # myLogger.exception("Error opening: %s!", some_url)
finally:
    try:
        s.close()
    # del s - although, I don't know if deleting s will help things any.
    except:
        pass

某些手册页和参考链接:

  

答案 1 :(得分:2)

解决! - 在Chown的大力帮助下 - 非常感谢你!

减速是因为我没有设置套接字超时,因此在一段时间内机器人挂起试图读取不存在的数据。添加一个简单的

timeout = 5
socket.setdefaulttimeout(timeout)

解决了它(对我很羞耻 - 但在我的辩护中,我还在学习python)

内存泄漏归结为urllib以及我正在使用的python版本。经过大量的谷歌搜索后,看来这是嵌套的urlopens的一个问题 - 当你弄清楚如何提出正确的谷歌问题时,很多关于它的帖子。

感谢大家的帮助。

编辑:

还有助于内存泄漏问题的事情(尽管没有完全解决)正在进行手动垃圾收集:

import gc
gc.collect

希望它可以帮助别人。

答案 2 :(得分:1)

这可能是你缺乏的一些系统资源。猜测:您能感受到系统可以处理的套接字池的限制吗?如果是,如果您可以更快/更快地关闭套接字,您可能会看到性能提升。

编辑:依赖于你想要的努力,你可以重组你的应用程序,使一个进程做多个请求。一个套接字可以在同一个进程中重用,也可以在很多不同的资源中重用。 Twisted非常适合这种类型的编程。

答案 3 :(得分:1)

要考虑的另一个系统资源是临时端口/proc/sys/net/ipv4/ip_local_port_range(在Linux上)。与/proc/sys/net/ipv4/tcp_fin_timeout一起,它们限制了并发连接的数量。

来自Benchmark of Python WSGI Servers

  

这基本上使服务器能够打开很多并发连接。

echo “10152 65535″ > /proc/sys/net/ipv4/ip_local_port_range
sysctl -w fs.file-max=128000
sysctl -w net.ipv4.tcp_keepalive_time=300
sysctl -w net.core.somaxconn=250000
sysctl -w net.ipv4.tcp_max_syn_backlog=2500
sysctl -w net.core.netdev_max_backlog=2500
ulimit -n 10240