Python脚本在套接字连接期间无限冻结

时间:2011-10-11 16:35:14

标签: python django sockets

我有一个简单的python脚本,用于更新数据库中justin.tv流的状态。这是一个基于Django的Web应用程序。在我将它移动到我的生产服务器之前,这个脚本运行得很好,但现在它有超时或冻结的问题。我通过添加try / except块并使脚本重试来解决超时问题,但我仍然无法弄清楚冻结问题。

我知道它冻结了streamOnline = manager.getStreamOnline(stream.name, LOG)行。这与发生socket.timeout异常的点相同。但有时候,它只是永远锁定。我无法想象python无限冻结的场景。以下是冻结脚本的代码。我正在链接下面的website.networkmanagers,以及我正在使用的oauth和justin.tv python库。

import sys, os, socket

LOG = False

def updateStreamInfo():
    # Set necessary paths
    honstreams = os.path.realpath(os.path.dirname(__file__) + "../../../")
    sys.path.append(honstreams)
    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

    # Import necessary moduels
    from website.models import Stream, StreamInfo
    from website.networkmanagers import get_manager, \
                                        NetworkManagerReturnedErrorException

    # Get all streams
    streams = Stream.objects.all()

    try:
        # Loop through them
        for stream in streams:

            skipstream = False

            print 'Checking %s...' % stream.name,
            # Get the appropriate network manager and
            manager = get_manager(stream.network.name)

            # Try to get stream status up to 3 times
            for i in xrange(3):
                try:
                    streamOnline = manager.getStreamOnline(stream.name, LOG)
                    break
                except socket.error as e:
                    code, message = e

                    # Retry up to 3 times
                    print 'Error: %s. Retrying...'

            # If this stream should be skipped
            if(skipstream):
                print 'Can\'t connect! Skipping %s' % stream.name
                continue

            # Skip if status has not changed
            if streamOnline == stream.online:
                print 'Skipping %s because the status has not changed' % \
                      stream.name
                continue

            # Save status
            stream.online = streamOnline
            stream.save()

            print 'Set %s to %s' % (stream.name, streamOnline)

    except NetworkManagerReturnedErrorException as e:
        print 'Stopped the status update loop:', e

if(__name__ == "__main__"):
    if(len(sys.argv) > 1 and sys.argv[1] == "log"):
        LOG = True

    if(LOG): print "Logging enabled"

    updateStreamInfo()

networkmanagers.py
oauth.py
JtvClient.py

冻结脚本的示例

  

foo @ bar:/.../ honstreams / honstreams#python website / scripts / updateStreamStatus.py
  检查angrytestie ...因为状态没有改变而跳过angrytestie   检查chustream ...因为状态没有改变而跳过chustream   检查cilantrogamer ...因为状态没有改变而跳过cilantrogamer   | < - Caret坐在这里无限地闪烁着


有趣的更新

每次冻结并发送键盘中断时,它都在 socket.py 的同一行:

root@husta:/home/honstreams/honstreams# python website/scripts/updateStreamStatus.py
Checking angrytestie... Skipping angrytestie because the status has not changed
Checking chustream... Skipping chustream because the status has not changed
^CChecking cilantrogamer...
Traceback (most recent call last):
  File "website/scripts/updateStreamStatus.py", line 64, in <module>
    updateStreamInfo()
  File "website/scripts/updateStreamStatus.py", line 31, in updateStreamInfo
    streamOnline = manager.getStreamOnline(stream.name, LOG)
  File "/home/honstreams/honstreams/website/networkmanagers.py", line 47, in getStreamOnline
    return self.getChannelLive(channelName, log)
  File "/home/honstreams/honstreams/website/networkmanagers.py", line 65, in getChannelLive
    response = client.get('/stream/list.json?channel=%s' % channelName)
  File "/home/honstreams/honstreams/website/JtvClient.py", line 51, in get
    return self._send_request(request, token)
  File "/home/honstreams/honstreams/website/JtvClient.py", line 90, in _send_request
    return conn.getresponse()
  File "/usr/lib/python2.6/httplib.py", line 986, in getresponse
    response.begin()
  File "/usr/lib/python2.6/httplib.py", line 391, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python2.6/httplib.py", line 349, in _read_status
    line = self.fp.readline()
  File "/usr/lib/python2.6/socket.py", line 397, in readline
    data = recv(1)
KeyboardInterrupt

有什么想法吗?

3 个答案:

答案 0 :(得分:0)

在JtvClient.py中,它使用httplib来处理连接。您是否尝试过将其更改为使用httplib2?

除了在黑暗中刺伤之外,我还会在此代码中添加大量日志记录语句,以便跟踪实际发生的情况以及卡住的位置。然后我会确保它被卡住的点可以在套接字上超时(这通常涉及monkeypatching或分叉代码库),这样东西就会失败而不是挂起。

你说:

  

我知道它在行streamOnline = manager.getStreamOnline(stream.name,LOG)上冻结了。这与发生socket.timeout异常的点相同。

错误。它不会冻结在该行上,因为该行是一个函数调用,它通过几个级别的其他模块调用许多其他函数。所以你还不知道程序冻结的地方。此外,该行是 NOT 发生套接字超时的点。套接字超时仅发生在低级别套接字操作(如select或recv)上,该操作在getStreamOnline触发的活动链中被多次调用。

您需要在调试器中跟踪代码或添加print语句以准确跟踪挂起的位置。它可能是Python中的无限循环,但更可能是对OS网络功能的低级调用。在找到错误来源之前,您无法做任何事情。

P.S。键盘中断是一个合理的线索,问题是在JtvClient.py中的第90行,所以放入一些打印语句并找出会发生什么。可能有一个愚蠢的循环,一直在调用getresponse,或者你可能用错误的参数调用它,或者网络服务器可能真的被borked。将其缩小到更少的可能性。

答案 1 :(得分:0)

您是否尝试过使用其他应用程序打开该连接?鉴于它是生产中的一个问题,也许你没有一些防火墙问题。

答案 2 :(得分:0)

事实证明此HTTP连接未在 jtvClient.py中传递超时

def _get_conn(self):
    return httplib.HTTPConnection("%s:%d" % (self.host, self.port))

将最后一行更改为

return httplib.HTTPConnection("%s:%d" % (self.host, self.port), timeout=10)

哪个解决了它