非阻塞地从HTTP流中读取/记录日志

6

我有一个客户端连接到一个HTTP流并记录其消耗的文本数据。

我向流服务器发送HTTP GET请求... 服务器回复并持续发布数据... 它将定期发布文本或发送ping(文本)消息...并且永远不会关闭连接。

我需要以非阻塞方式读取和记录其消耗的数据。

我正在进行以下操作:

import urllib2

req = urllib2.urlopen(url)    
for dat in req: 
    with open('out.txt', 'a') as f:        
        f.write(dat) 

我的问题是:
当流是连续的时候,这个方法会阻塞吗?
每个块读取多少数据,是否可以指定/调整?
这是读取/记录HTTP流的最佳方法吗?

4个回答

6

嘿,这是三个问题!;-)

它有时可能会阻塞 - 即使您的服务器生成数据相当快,理论上网络瓶颈也可能导致读取阻塞。

使用“for dat in req”读取URL数据意味着一次读取一行 - 如果您正在读取二进制数据(例如图像),则不是真正有用的。如果您使用

chunk = req.read(size)

这当然可以阻塞。

是否是最佳方法取决于您问题中不可用的具体情况。例如,如果您需要完全没有阻塞调用地运行,则需要考虑像Twisted这样的框架。如果您不希望阻塞耽误您,也不想使用Twisted(与阻塞方式相比是一个全新的范例),那么您可以启动一个线程来读写文件,而主线程则继续执行:

def func(req):
    #code the read from URL stream and write to file here

...

t = threading.Thread(target=func)
t.start() # will execute func in a separate thread
...
t.join() # will wait for spawned thread to die

显然,我省略了错误检查/异常处理等,但希望这足以给您提供一个概念。


3
您使用的是太高级的接口,无法很好地控制阻止和缓冲块大小等问题。如果您不想完全转向异步接口(在这种情况下,已经建议使用twisted),为什么不考虑使用httplib呢?毕竟它是标准库中的一部分。HTTPResponse实例的.read(amount)方法更有可能只阻止读取amount字节所需的时间,而与urlopen返回的对象相似的方法则不然(尽管两个模块都没有记录规格,嗯...)。

2

另一种选择是直接使用socket模块。建立连接、发送HTTP请求、将socket设置为非阻塞模式,然后使用socket.recv()读取数据,并处理“资源暂时不可用”异常(这意味着没有可读取的内容)。一个非常简单的示例代码如下:

import socket, time

BUFSIZE = 1024

s = socket.socket()
s.connect(('localhost', 1234))
s.send('GET /path HTTP/1.0\n\n')
s.setblocking(False)

running = True

while running:
    try:
        print "Attempting to read from socket..."
        while True:
            data = s.recv(BUFSIZE)
            if len(data) == 0:      # remote end closed
                print "Remote end closed"
                running = False
                break
            print "Received %d bytes: %r" % (len(data), data)
    except socket.error, e:
        if e[0] != 11:      # Resource temporarily unavailable
            print e
            raise

    # perform other program tasks
    print "Sleeping..."
    time.sleep(1)

然而,如果Web服务器进行重定向、需要基于URL的基本验证等情况,urllib.urlopen()具有一些好处。你可以利用select模块来告诉你何时有数据可读。


1

是的,当你追上服务器时,它会阻塞直到服务器产生更多数据。

每个数据将成为一行,包括结尾的换行符。

twisted 是一个好选择。

在您的示例中,我建议将 with 和 for 调换位置,您真的想为每个到达的行打开和关闭文件吗?


for/with的顺序是有意为之的。这将在每次写入时打开/关闭文件句柄。对于繁忙的流来说效率不高,但在我的情况下,该流大多被阻塞/等待,然后偶尔接收数据进行日志记录。 - Corey Goldberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接