简体   繁体   English

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

[英]Python script freezes infinitely during a socket connection

I have a simple python script that updates that statuses of justin.tv streams in my database. 我有一个简单的python脚本,用于更新数据库中justin.tv流的状态。 It's a Django based web application. 这是一个基于Django的Web应用程序。 This script worked perfectly before I moved it to my production server, but now it has issues with timing out or freezing. 在我将其移至生产服务器之前,该脚本可以完美运行,但是现在它存在超时或冻结的问题。 I've solved the time out problem by adding try/except blocks and making the script retry, but I still can't figure out the freezing problem. 我已经通过添加try / except块并重试脚本来解决了超时问题,但仍然无法解决冻结问题。

I know it freezes on the line streamOnline = manager.getStreamOnline(stream.name, LOG) . 我知道它冻结在streamOnline = manager.getStreamOnline(stream.name, LOG) That's the same point where the socket.timeout exception occurs. 那就是socket.timeout异常发生的同一点。 Some times however, it just locks up for ever. 但是有时候,它只是永远锁定。 I just can't picture a scenario where python would freeze infinitely. 我只是无法想象python会无限冻结的情况。 Here is the code for the script that freezes. 这是冻结的脚本的代码。 I'm linking website.networkmanagers below, as well as oauth and the justin.tv python library that I'm using. 我正在链接下面的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 networkmanagers.py
oauth.py oauth.py
JtvClient.py JtvClient.py

Example of the script freezing 脚本冻结的示例

foo@bar:/.../honstreams/honstreams# python website/scripts/updateStreamStatus.py foo @ bar:/.../ honstreams / honstreams#python网站/脚本/updateStreamStatus.py
Checking angrytestie... Skipping angrytestie because the status has not changed 正在检查Angrytestie ...正在跳过Angrytestie,因为状态未更改
Checking chustream... Skipping chustream because the status has not changed 正在检查chustream ...正在跳过chustream,因为状态尚未更改
Checking cilantrogamer... Skipping cilantrogamer because the status has not changed 正在检查cilantrogamer ...正在跳过cilantrogamer,因为状态未更改
| | <- caret sits here blinking infinitely <-插入符号坐在这里无限闪烁


Interesting update 有趣的更新

Every time it freezes and I send a keyboard interrupt, it's on the same line in socket.py : 每次冻结并发送键盘中断时,它都在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

Any thoughts? 有什么想法吗?

Down in JtvClient.py it uses httplib to handle the connection. 在JtvClient.py中,它使用httplib处理连接。 Have you tried changing this to use httplib2 instead? 您是否尝试过将其改为使用httplib2?

Other than that stab in the dark, I would add a lot of logging statements to this code in order to track what actually happens and where it gets stuck. 除了黑暗中的刺伤之外,我还会在此代码中添加许多日志记录语句,以便跟踪实际发生的情况以及它在哪里卡住。 Then I would make sure that the point where it gets stuck can timeout on the socket (which usually involves either monkeypatching or forking the codebase) so that stuff fails instead of hanging. 然后,我要确保它卡住的点可以在套接字上超时(通常涉及到猴子补丁或派生代码库),以便使东西失败而不是挂起。

You said: 你说:

I know it freezes on the line streamOnline = manager.getStreamOnline(stream.name, LOG). 我知道它冻结在streamOnline = manager.getStreamOnline(stream.name,LOG)行上。 That's the same point where the socket.timeout exception occurs. 那就是socket.timeout异常发生的同一点。

Wrong. 错误。 It doesn't freeze on that line because that line is a function call which calls lots of other functions through several levels of other modules. 它不会冻结在该行上,因为该行是一个函数调用,它通过多个其他模块级别调用许多其他函数。 So you do not yet know where the program freezes. 因此,您尚不知道程序在哪里冻结。 Also, that line is NOT the point where the socket timeout occurs. 另外,该行不是套接字超时发生的地方。 The socket timeout will only occur on a low level socket operation like select or recv which is being called several times in the chain of activity triggered by getStreamOnline. 套接字超时仅在诸如select或recv之类的低级套接字操作上发生,该操作在由getStreamOnline触发的活动链中被多次调用。

You need to trace your code in a debugger or add print statements to track down exactly where the hang occurs. 您需要在调试器中跟踪代码或添加打印语句以精确跟踪挂起的位置。 It could possibly be an infinite loop in Python but is more likely to be a low-level call to an OS networking function. 它在Python中可能是无限循环,但更可能是对OS网络功能的低级调用。 Until you find the source of the error, you can't do anything. 在找到错误源之前,您将无法做任何事情。

PS the keyboard interrupt is a reasonable clue that the problem is around line 90 in JtvClient.py, so put in some print statements and find out what happens. PS键盘中断是一个合理的线索,表明问题出在JtvClient.py的第90行附近,因此请输入一些打印语句并找出会发生什么情况。 There may be a stupid loop in there that keeps calling getresponse, or you may be calling it with bad parameters or maybe the network server really is borked. 那里可能存在一个愚蠢的循环,不断调用getresponse,或者您用错误的参数调用了它,或者网络服务器确实很烦。 Narrow it down to fewer possibilities. 将其缩小到更少的可能性。

Have you tried using another application to open that connection? 您是否尝试过使用其他应用程序打开该连接? Given that it's an issue in production, perhaps you don't have some firewall issues. 考虑到这是生产中的问题,也许您没有防火墙的问题。

It turns out this HTTP connection isn't passed a timeout in jtvClient.py 事实证明,此HTTP连接未在jtvClient.py中传递超时

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

Changed the last line to 将最后一行更改为

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

Which solved it 哪个解决了

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM