龙卷风:如何以更少的内存使用量获取和返回大数据?

时间:2016-09-25 13:40:26

标签: python tornado

我有网络抓取工具和http接口。

抓取工具将分组的网址设为字典。我需要在JSON中以相同的格式返回结果。但我面临着大量的内存使用,而不是返回给操作系统。如何在没有大量内存使用的情况下实现此解决方案?

代码:

#!/usr/bin/env python
# coding=utf-8

import collections

import tornado.web
import tornado.ioloop
import tornado.queues
import tornado.httpclient


class ResponseError(Exception):

    pass


class Crawler(object):

    client = tornado.httpclient.AsyncHTTPClient()

    def __init__(self, groups, concurrency=10, retries=3, validators=None):
        self.groups = groups
        self.concurrency = concurrency
        self.retries = retries
        self.validators = validators or []

        self.requests = tornado.queues.Queue()
        self.responses = collections.defaultdict(list)

    async def worker(self):
        while True:
            await self.consume()

    async def validate(self, response):
        for validator in self.validators:
            validator(response)

    async def save(self, response):
        self.responses[response.request.group].append(response.body.decode('utf-8'))

    async def consume(self):
        async for request in self.requests:
            try:
                response = await self.client.fetch(request, raise_error=False)

                await self.validate(response)
                await self.save(response)
            except ResponseError:
                if request.retries < self.retries:
                    request.retries += 1
                    await self.requests.put(request)
            finally:
                self.requests.task_done()


    async def produce(self):
        for group, urls in self.groups.items():
            for url in urls:
                request = tornado.httpclient.HTTPRequest(url)
                request.group = group
                request.retries = 0
                await self.requests.put(request)

    async def fetch(self):
        await self.produce()

        for __ in range(self.concurrency):
            tornado.ioloop.IOLoop.current().spawn_callback(self.worker)

        await self.requests.join()



class MainHandler(tornado.web.RequestHandler):

    async def get(self):
        urls = []

        with open('urls') as f:  # mock
            for line in f:
                urls.append(line.strip())

        crawler = Crawler({'default': urls})

        await crawler.fetch()

        self.write(crawler.responses)


if __name__ == '__main__':
    app = tornado.web.Application(
        (tornado.web.url(r'/', MainHandler),), debug=True
    )
    app.listen(8000)

    tornado.ioloop.IOLoop.current().start()

1 个答案:

答案 0 :(得分:0)

在我看来,大多数内存使用都用于self.responses。因为你似乎在订购&#34; group&#34;在将它们写入文件之前,我可以理解为什么这样做。一个想法是使用&#34;组&#34;将它们存储在数据库(MySQL或MongoDB或其他)中。作为数据库记录中的列或字段值。

数据库可能是数据的最终目标,或者在crawler.fetch完成之前,它可能是存储数据的临时位置。然后,查询数据库中的所有数据,按&#34; group&#34;排序,并将其写入文件。

这并不是解决问题,它只是意味着数据库进程负责大部分内存使用,而不是Python进程。但是,这可能对您更有利。