Используйте python, должен ли я кэшировать большие данные в массиве и записывать в файл за один раз?

У меня постоянно есть страницы загрузки поискового робота с питанием от gevent. Сканер использует шаблон производителя-потребителя, который я загружаю в очередь данными, подобными этому {method:get, url:xxxx, other_info:yyyy}.

Теперь я хочу собрать ответ в файлы. Проблема в том, что я не могу просто открыть и написать, когда каждый запрос заканчивается, это дорого и данные не в правильном порядке.

Я предполагаю, что я должен пронумеровать все запросы, кэшировать ответы по порядку, открыть гринлет для цикла и собрать файлы, псевдокод может быть таким:

max_chunk=1000
data=[]
def wait_and_assemble_file(): # a loop
    while True:
        if len(data)==28:
            f= open('test.txt','a')
            for d in data:
                f.write(d)
            f.close()
        gevent.sleep(0)

def after_request(response, index): # Execute after every request ends
    data[index]=response  # every response is about 5-25k

Есть ли лучшее решение? Есть тысячи одновременных запросов, и я сомневаюсь, что использование памяти может расти слишком быстро, или слишком много циклов за один раз, или что-то неожиданное.

Обновление:

Приведенные выше коды просто демонстрируют, как работает кэширование данных и запись в файл. На практике может потребоваться 100 циклов для ожидания завершения кэширования и записи в разные файлы.

Обновление 2

@IT Ninja предлагает использовать систему очередей, поэтому я пишу альтернативу, используя Redis:

def after_request(response, session_id, total_block_count ,index): # Execute after every request ends
    redis.lpush(session_id, msgpack.packb({'index':index, 'content':response}))  # save data to redid

    redis.incr(session_id+':count')
    if redis.get(session_id+':count') == total_block_count: # which means all data blocks are prepared
        save(session_name)


def save(session_name):
  data_array=[]
  texts = redis.lrange(session_name,0,-1)
  redis.delete(session_name)
  redis.delete(session_name+':count')
  for t in texts:
    _d = msgpack.unpackb(t)
    index = _d['index']
    content = _d['content']
    data_array[index]=content

  r= open(session_name+'.txt','w')
  [r.write(i) for i in data_array]
  r.close()

Выглядит немного лучше, но я сомневаюсь, что сохранение больших данных в Redis — хорошая идея, надеюсь на дополнительные предложения!


person Patrick Z    schedule 07.11.2013    source источник
comment
Как насчет того, чтобы открыть файл один раз и оставить его открытым? (но я не знаю, что произойдет в случае сбоя вашей программы). И я не уверен, что кэширование перед записью полезно, так как ваша операционная система уже может выполнять кэширование сама по себе.   -  person Bas Swinckels    schedule 07.11.2013
comment
моя ОС ubuntu и булавы. Помимо кэширования, мне нужно изменить порядок данных, так как они извлекаются асинхронно без порядка. @BasSwinckels   -  person Patrick Z    schedule 07.11.2013


Ответы (2)


Нечто подобное может быть лучше обработано с помощью системы очередей, вместо того, чтобы каждый поток имел свой собственный обработчик файлов. Это связано с тем, что вы можете столкнуться с условиями гонки при записи этого файла из-за того, что каждый поток имеет свой собственный обработчик.

Что касается ресурсов, это не должно потреблять слишком много ресурсов, кроме записи на ваш диск, при условии, что информация, передаваемая в файл, не очень велика (Python действительно хорош в этом). Однако, если это создает проблему, чтение в память файла фрагментами (и пропорционально запись фрагментами) может значительно уменьшить эту проблему, если это доступно в качестве опции для загрузки файлов.

person IT Ninja    schedule 07.11.2013
comment
Запись в разные файлы, не конфликтует. - person Patrick Z; 07.11.2013
comment
Вы имеете в виду сохранение ответа на внешнюю систему, такую ​​​​как Redis? Кажется, лучше, так что программе не нужно поддерживать очень большой массив данных. Я обновлю свой пост, используя redid, пожалуйста, проверьте! - person Patrick Z; 07.11.2013
comment
да, вы можете передать его в Redis, а затем иметь работника, который проверяет Redis на наличие данных и записывает в нужный файл (файлы). Таким образом, ваш веб-сервер абстрагируется от файлового ввода-вывода, и веб-сервер не касается файлов ни в одном из потоков. Насколько я помню, это может работать очень хорошо, потому что эта абстракция позволяет вам экспоненциально масштабироваться без изменения кода веб-сервера. - person IT Ninja; 07.11.2013
comment
Я обновил код с помощью Redis. Нижняя муфта хороша для больших систем, в меньших, как у меня, здесь ИМО не очень важна. Но я просто не знаю, в чем преимущество использования Redis. - person Patrick Z; 07.11.2013
comment
Возможно, большинство из них более масштабируемы, поскольку Redis масштабируем. - person Patrick Z; 07.11.2013
comment
Что ж, как вы сказали в своем комментарии к своему сообщению, поскольку вам все равно нужно переупорядочивать данные, это идеальная работа, которую нужно выполнять в фоновом режиме, а не в потоках веб-сервера, потому что это может быть чрезвычайно дорого с точки зрения ресурсов. для того, чтобы вернуть ответ в разумные сроки. - person IT Ninja; 07.11.2013

Это зависит от размера данных. Если он очень большой, это может замедлить работу программы, поскольку вся структура находится в памяти.

Если память не является проблемой, вы должны хранить структуру в памяти, а не читать все время из файла. Открытие файла снова и снова с одновременным запросом не является хорошим решением.

person dseira    schedule 07.11.2013
comment
каждый файл состоит из 1000 блоков, всего размером 10M. Как вы говорите, я избегаю открывать файл снова и снова, так как операция ввода-вывода стоит дорого. И что более важно, мне нужно переупорядочить блоки данных в правильной последовательности. - person Patrick Z; 07.11.2013