2015-09-16 3 views
3

У меня есть код, который генерирует тысячи счетов-фактур в виде PDF-файлов, а затем записывает их в zip-файл и затем выкачивает их поверх HttpStreamingResponse (django). Генерация PDF действительно медленная и в настоящее время однопоточная.Работа с многопоточным выходом последовательно

Я могу быстро сгенерировать исходный HTML для PDF-файлов. Я хотел бы:

  • Сформировать все HTML (однопоточный, база данных не может обрабатывать одновременно Lookups)
  • Преобразовать эти в PDF-файлы, в 8 потоков
  • Синхронно обрабатывать выход из PDF-файлов поэтому я могу добавить его в файл zipstream.

Я занимался многопроцессорной обработкой.Надеюсь, но я не знаю, как это сделать должным образом. Вот приблизительный код.

def generate_statements(request): 
    htmls = [generate_html(customer) for customer in customers] 
    pdfs = [generate_pdf(html) for html in htmls] 

    # create zip file 

    for pdf in pdfs: 
     zip.writestring(...) 

    # output this to browser 

def generate_html(customer): 
    # do something that returns a string of HTML 

def generate_pdf(html): 
    # do something that creates a single pdf 

Если есть возможность, чтобы начать преобразование HTML перед HTMLs закончены, еще лучше, но мне нужно обрабатывать вывод generate_pdf линейным способом; Я не могу писать в zip одновременно.

(PS: Я понимаю, что некоторые из этих вещей может показаться, как домашнее задание, но, пожалуйста, посмотрите на мой профиль сети, прежде чем предположить, что я ленивый студент ... Я ленивый профессиональный программист thankyouverymuch)

+0

Я бы с [резьб] (https://docs.python.org/dev/library/threading.html) и [очередей] (https://docs.python.org /dev/library/queue.html). Я полагаю, что фактическая работа по созданию PDF-файлов выполняется с помощью внешней программы; Если это так, я бы избегал многопроцессорной обработки (https://docs.python.org/dev/library/multiprocessing.html), поскольку он добавляет лишние накладные расходы. –

+0

«база данных не может обрабатывать параллельные запросы» - получение лучшей базы данных может быть хорошим началом ... – twalberg

ответ

1

Это проще. Следующий ответ напомнил мне метод многопроцессорной карты пула. Это асинхронная карта - точно так же, как встроенная карта. Он присоединится (то есть - он не вернется, пока все асинхронные вещи не будут выполнены), и вы получите упорядоченный список всех предметов.

htmls = [generate_html(customer) for customer in customers] 
print "Htmls is:" , repr(htmls) 
print "Starting map" 
pdf_pool = Pool(5) 
pdfs = pdf_pool.map(generate_pdf, htmls, 8) 

print "Done map" 
# zip stuff 
for pdf in pdfs: 
    print(pdf) 
+0

Не могли бы вы немного расшириться? Я видел много очередей «Очередь», в то время как Googing для этого, но, похоже, он используется больше как источник, чем раковина. Я бы передал его в качестве аргумента, но как мне запустить очередь в моем синхронном коде? – Oli

0
from multiprocessing import Pool 
from time import sleep 

# html list that has to convert to pdf 
jobs = range(0,100000) 

def create_pdf(html,zipstream): 
    print 'starting job {}'.format(html) 
    pdf = convert_html_to_pdf(html) # finished converting, returns location of the pdf 
    try untill suceed: #implement something that checks lock on zipstream 
     zipstream.write(pdf) 
    sleep(2) 
    print 'ended job {}'.format(html) 
    #do whatever, followed by whatever requirement for this html. 


pool = Pool(processes=8) 
with ZipFile('pdfs.zip', 'w') as myzip: 
    print pool.map(create_pdf,(jobs,myzip)) # jobs is list of job 
    #... when done, 
    zipstream.close() 
+0

My create_pdf нужно написать то, с чем может работать мой синхронный код (писать в zip-файл). В этом проблема. – Oli

+0

Не то, чтобы я «смешивал» их, это то, что я на самом деле не храню PDF-файлы на диске, мое поколение PDF возвращает их как bytestring, поэтому я могу спрятать их прямо в zip. – Oli

+0

Я отредактировал ответ, посмотрим, работает ли эта логика с вашими требованиями. – taesu

Смежные вопросы