了解并发进程

时间:2017-01-31 18:42:25

标签: python concurrency python-multiprocessing

我正在构建一个执行某个IO绑定任务的脚本。我需要它来尝试下载大量数据集,在丢弃数据本身之前记录有关其大小的某些信息。

问题是我从这些数据获取的数据源并不提供content-length标题,因此无法事先知道文件大小有多大。这需要我找到一种方法来监视下载过程需要多长时间,并且有一种方法可以杀死该过程并继续执行其他此类过程,以防它花费太长时间(比例,超过60秒)。这是必要的,以避免卡住"在非常大的数据集上。

requests没有内置此功能,在花费大量时间搜索解决方案后,我决定通过pebble库运行并发进程并暂停。我的理解是,这是对标准库multiprocessing模块的一个小扩展,它增加了一些安全功能,即错误处理和超时(这就是我想要的)。

基于Process pool示例,这是我的代码:

try:
    with ProcessPool(max_workers=4) as pool:
        iterator = pool.map(get_data, process_tuples[3:6], timeout=10)

        while True:
            try:
                rows, cols, filesize, i = next(iterator)
                datasets[i]['rows'] = rows
                datasets[i]['columns'] = cols
                datasets[i]['filesize'] = filesize
            except TimeoutError as error:
                print("Function took longer than %d seconds. Skipping responsible endpoint..." % error.args[1])
            except StopIteration:
                break
finally:
    with open("../../../data/" + FILE_SLUG + "/glossaries/geospatial.json", "w") as fp:
        json.dump(datasets, fp, indent=4)

但这有两个方面与预期行为不同:

  1. 我原以为timeout=10限制了每个下载过程(由get_data完成)所花费的时间。但是,当我在一个大文件上运行它时,我收到一个TimeoutError,表明我的进程花了超过30秒。 30是我输入长度的3倍;这根本不是我想要的。那里发生了什么?
  2. TimeoutError被提升时,过程会跳转到finally区块(我不想要的),而不是丢弃该项运行并转移到下一个区域(我想要的) 。我认为这是我第一个问题答案的结果。

1 个答案:

答案 0 :(得分:1)

实际上在requests中,您可以设置 stream=True 并使用 Response.iter_content() 来进一步控制工作流程。

在您的情况下,我们可以跟踪下载/迭代响应数据时所经过的时间:

import time
import requests

def get_content(url, timeout):
    """
    Get response data from url before timeout
    """
    start = time.time()
    data = ''
    response = requests.get(url, stream=True)

    for chunk in response.iter_content(chunk_size = 1024): # You can set a bigger chunk_size for less iterations
        if (time.time() - start) > timeout:
            response.close()
            return {'TimedOut': True, 'data': None}
        else:
            data += chunk

    response.close()
    return {'TimedOut': False, 'data': data}

所以基本上你设置了一个timeout值,如果数据太大或网络太慢,一旦花费超过timeout,结果将被返回,那些不完整的数据将是垃圾收集。

接下来,由于它是IO限制任务,我们可以使用threadingmultiprocessing来完成工作,这里是使用threading的示例

import threading, Queue

def worker(queue):
    while not queue.empty():
        url = queue.get()

        result = get_content(url, 60)

        # Do other stuff

if __name__ == '__main__':
    limit = 10 # number of threads to use
    thread_pool = [None] * limit
    queue = Queue.Queue()
    urls = ['xxxx', 'xxxxx']

    for url in urls:
        queue.put(url)

    for thread in thread_pool:
        thread = threading.Thread(target=worker, args=(queue, ))
        thread.start()