Django_-_podrobnoe_rukovodstvo
.pdf
270 |
Глава 12. Развертывание Django |
Оптимизация производительности
Если вы не ограничены в деньгах, то можете решать проблемы масштабирования, покупая все новое и новое оборудование. Ну а для большинства из нас необходимостью становится оптимизация производительности.
Примечание
Кстати, если вы такой богатей, может, пожертвуете кругленькую сумму в фонд Django? Необработанные алмазы и золотые слитки мы тоже принимаем.
К сожалению, оптимизация производительности – скорее, искусство, нежели наука, и писать об этом еще труднее, чем о масштабировании. Если вы серьезно вознамерились развернуть крупномасштабное приложение Django, то должны будете потратить немало времени на изучение настройки каждого компонента стека.
В следующих разделах мы просто расскажем о некоторых специфических для Django приемах, которые выработали с годами.
Памяти много не бывает
Даже самая дорогая оперативная память в наши дни вполне доступна. Купите столько памяти, сколько можете себе позволить, а потом еще немножко.
Более быстрый процессор не даст такого прироста производительности; до 90% времени веб-сервер тратит на ожидание завершения операций дискового ввода/вывода. Как только начинается свопинг, с надеждами на высокую производительность можно распрощаться. Скоростные диски немного улучшают ситуацию, но они гораздо дороже памяти, так что игра не стоит свеч.
При наличии нескольких серверов нарастите объем памяти прежде всего на сервере базы данных. Если можете себе позволить, купите столько памяти, чтобы в нее помещалась вся база данных целиком. Это не такая уж несбыточная мечта; мы как-то разработали сайт, на котором хранилось более полумиллиона газетных статей, и на все потребовалось менее 2 Гбайт памяти.
Затем добавьте памяти веб-серверу. В идеале свопинг не должен возникать ни на одном сервере – никогда. Если вы сумеете это обеспечить, то почти наверняка сможете справиться с любым возможным трафиком.
Отключите режим Keep-Alive
Режим Keep-Alive – это встроенная в протокол HTTP возможность обслуживать несколько запросов по одному TCP-соединению, избегая накладных расходов на установление и разрыв соединения.
Что дальше? |
271 |
На первый взгляд, выглядит неплохо, но может свести на нет все попытки повысить производительность Django-сайта. Если вы правильно настроите обслуживание мультимедийного содержимого с отдельного сервера, то любой пользователь, посетивший ваш сайт, будет запрашивать страницу с сервера Django примерно раз в десять секунд. В результате HTTP-серверы будут простаивать, ожидая следующего запроса на открытом соединении и при этом потребляя память, которая очень пригодилась бы активному серверу.
Используйте Memcached
Хотя Django поддерживает разные механизмы кэширования, тем не менее ни один из них даже близко не сравнится по скорости с Memcached. Если сайт испытывает высокую нагрузку, то даже не пробуйте другие механизмы – сразу обращайтесь к Memcached.
Используйте Memcached как можно чаще
Разумеется, выбор Memcached ничего не даст, если им не пользоваться. Тут вам на помощь придет глава 15; выясните, как работает подсистема кэширования в Django, и применяйте ее всюду, где возможно. Всепроникающее кэширование с вытеснением – обычно единственное, что помогает справиться с высокой нагрузкой.
Присоединяйтесь к диалогу
За каждым компонентом стека Django, будь то Linux, Apache, Postgre SQL или MySQL, стоит впечатляющее сообщество. Если вы по-насто ящему хотите выжать из своего сервера всю производительность до последней капли, присоединяйтесь к сообществу и обращайтесь за помощью. Большинство участников сообществ пользователей программ
соткрытым исходным кодом будут только рады помочь.
Иобязательно вступите в сообщество пользователей Django. Авторы этой книги – всего лишь два члена невероятно активной и растущей группы разработчиков Django. Наше сообщество может поделиться огромным накопленным коллективным опытом.
Что дальше?
В последующих главах мы расскажем о других возможностях Django, которые могут пригодиться или нет в зависимости от приложения. Можете читать их в любом порядке.
III
Прочие возможности Django
13
Создание содержимого в формате, отличном от HTML
Обычно, говоря о разработке сайтов, мы имеем в виду создание HTMLдокументов. Но не HTML’ем единым славен Интернет. Посредством Интернета мы распространяем данные в самых разных форматах: RSS, PDF, графика и т. д.
До сих пор мы рассматривали только воспроизведение HTML – самый распространенный случай, но в этой главе отойдем немного в сторону и покажем, как с помощью Django генерировать содержимое других видов.
В Django имеются встроенные средства для создания содержимого в некоторых часто встречающихся форматах:
•• Ленты новостей в формате RSS/Atom.
•• Карты сайтов (в формате XML, который первоначально был разработан компанией Google для предоставления дополнительной информации поисковым системам) .
Мы рассмотрим эти средства ниже, но сначала поговорим о принципах.
Основы: представления и типы MIME
Напомним (см. главу 3), что представление – это обычная функция Python, которая принимает веб-запрос и возвращает веб-ответ. Ответом может быть HTML-разметка страницы, переадресация, ошибка 404, XML-документ, изображение, вообще все что угодно.
Если говорить формально, то функция представления в Django должна:
•• Принимать объект класса HttpRequest в качестве первого аргумента.
•• Возвращать объект класса HttpResponse.
276 |
Глава 13. Создание содержимого в формате, отличном от HTML |
Ключом к возврату содержимого в формате, отличном от HTML, является класс HttpResponse, а точнее, его атрибут mimetype. С помощью типа MIME мы сообщаем броузеру о формате возвращаемого ответа.
Рассмотрим, к примеру, представление, возвращающее изображение в формате PNG. Чтобы не усложнять задачу, будем считать, что оно читается из файла на диске.
from django.http import HttpResponse
def my_image(request):
image_data = open(“/path/to/my/image.png”, “rb”).read() return HttpResponse(image_data, mimetype=”image/png”)
Вот и все! Заменив путь в вызове open(), вы сможете использовать это простенькое представление для возврата любого изображения, и броузер корректно отобразит его.
Еще один важный момент состоит в том, что объекты HttpResponse реализуют стандартный API, который обычно используется для доступа к файлам. Это означает, что такой объект можно использовать всюду, где Python (или сторонняя библиотека) ожидает получить файл.
Чтобы понять, как можно использовать это обстоятельство, рассмотрим создание CSV-ответа средствами Django.
Создание ответа в формате CSV
CSV – это простой формат данных, который часто применяется в электронных таблицах. По существу, это последовательность строк таблицы, в которой ячейки разделяются запятыми (CSV означает comma-separated values, то есть значения, разделенные запятыми). Вот, например, некоторые данные о «буйных» авиапассажирах в формате CSV:
Год,Количество буйных авиапассажиров 1995,146 1996,184 1997,235 1998,200 1999,226 2000,251 2001,299 2002,273 2003,281 2004,304 2005,203 2006,134 2007,147
Примечание
Приведенные в этом перечне данные реальны. Они получены от Федерального управления гражданской авиации США.
Создание ответа в формате CSV |
277 |
Хотя формат CSV выглядит очень простым, некоторые детали так и не согласованы до конца. Разные программы создают и принимают данные в разных вариантах CSV, из-за чего работа с ними несколько осложняется. К счастью, в состав стандартного дистрибутива Python уже входит библиотека csv для работы с этим форматом, в которой учтено большинство нюансов.
Поскольку модуль csv оперирует объектами, используя API доступа к файлам, ему можно «подсунуть» и HttpResponse:
import csv
from django.http import HttpResponse
#Количество буйных пассажиров за годы с 1995 по 2007. В реальном
#приложении данные, скорее всего, брались бы из базы или иного
#внешнего хранилища.
UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273, 281,304,203, 134, 147]
def unruly_passengers_csv(request):
#Создать объект HttpResponse с заголовком, описывающим формат
#CSV.
response = HttpResponse(mimetype=’text/csv’) response[‘Content-Disposition’] = ‘attachment; filename=unruly.csv’
# Создать объект вывода CSV, используя HttpResponse как “файл”. writer = csv.writer(response)
writer.writerow([‘Year’, ‘Unruly Airline Passengers’])
for (year, num) in zip(range(1995, 2007), UNRULY_PASSENGERS): writer.writerow([year, num])
return response
Код и комментарии к нему достаточно прозрачны, но некоторые моменты все же заслуживают упоминания.
•• Для ответа задан тип MIME text/csv (а не принимаемый по умолчанию text/html). Тем самым мы сообщаем броузеру, что это документ в формате CSV.
•• В ответ был добавлен дополнительный заголовок Content-Disposition, содержащий имя CSV-файла. Этот заголовок (точнее, «вложение») говорит броузеру, что файл следует сохранить, а не просто отобразить. Имя файла может быть произвольным, броузер выведет его в окне диалога «Сохранить как».
•• Чтобы добавить в ответ HttpResponse новый заголовок, нужно рассматривать этот объект как словарь и определить соответствующие ключ и значение.
•• При обращении к API для работы с CSV мы передаем response в качестве первого аргумента конструктору класса writer, который ожидает получить «файлоподобный» объект, а HttpResponse вполне подходит на эту роль.
278 |
Глава 13. Создание содержимого в формате, отличном от HTML |
•• Для каждой строки CSV-файла мы вызываем метод writer.writerow, передавая ему итерируемый объект, например, список или кортеж.
•• Модуль CSV сам позаботится о расстановке кавычек, так что вы можете не беспокоиться по поводу строк, содержащих кавычки или запятые. Просто передайте данные методу writerow(), и он все сделает правильно.
Следующий общий алгоритм выполняется всякий раз, когда требуется вернуть содержимое в формате, отличном от HTML: создаем объект HttpResponse (с нужным типом MIME), передаем его какому-нибудь методу, ожидающему получить файл, и возвращаем ответ.
Рассмотрим другие примеры.
Генерация ответа в формате PDF
Формат Portable Document Format (PDF – формат переносимых документов) разработан компанией Adobe для представления документов, предназначенных для печати. Он поддерживает размещение с точностью до пиксела, вложенные шрифты и двумерную векторную графику. Документ в формате PDF можно считать цифровым эквивалентом печатного документа; действительно, документы, предназначенные для печати, очень часто распространяются в этом формате.
Python и Django позволяют без труда создавать PDF-документы благодаря великолепной библиотеке ReportLab (http://www.reportlab.org/rl_ toolkit.html). Достоинство динамического создания PDF в том, что можно создавать специализированные документы для разных целей, например, свой для каждого пользователя или с разным содержимым.
Например, авторы применяли Django и ReportLab на сайте KUsports. com для создания документов, содержащих турнирные сетки соревнований по баскетболу, проводимых Национальной студенческой спортивной ассоциацией.
Установка ReportLab
Прежде чем приступать к созданию PDF, необходимо установить библиотеку ReportLab. Ничего сложного в этом нет, просто загрузите ее со страницы http://www.reportlab.org/downloads.html и установите на свой компьютер.
Примечание
Если вы пользуетесь современным дистрибутивом Linux, сначала проверьте, нет ли в нем уже готового пакета. Пакет ReportLab уже включен в состав большинства репозиториев. Например, в случае Ubuntu достаточно выполнить команду apt-get install python-reportlab.
Генерация ответа в формате PDF |
279 |
В руководстве пользователя (естественно, в формате PDF) по адресу http://www.reportlab.org/rsrc/userguide.pdf имеются дополнительные инструкции по установке.
Проверьте правильность установки, импортировав библиотеку в интерактивном интерпретаторе Python:
>>> import reportlab
Если команда выполнится без ошибок, значит, установка прошла нормально.
Создание собственного представления
Как и в случае с форматом CSV, динамическое создание PDF в Django не вызывает сложностей, так как для работы с объектами библиотека ReportLab использует API доступа к файлам:
Ниже приводится простенький пример «Hello World»:
from reportlab.pdfgen import canvas from django.http import HttpResponse
def hello_pdf(request):
#Создать объект HttpResponse с заголовками для формата PDF. response = HttpResponse(mimetype=’application/pdf’) response[‘Content-Disposition’] = ‘attachment; filename=hello.pdf’
#Создать объект PDF, передав объект ответа в качестве “файла”.
p = canvas.Canvas(response)
#Нарисовать нечто в PDF. Именно здесь происходит создание
#содержимого PDF-документа.
#Полное описание функциональности см. в документации ReportLab. p.drawString(100, 100, “Hello world.”)
#Закрыть объект PDF, все готово.
p.showPage()
p.save() return response
Здесь будет уместно сделать несколько замечаний:
•• Мы указали тип MIME application/pdf и тем самым сообщили броузеру, что это документ в формате PDF, а не HTML. Если опустить эту информацию, то броузер попытается интерпретировать ответ как страницу HTML и выведет на экран белиберду.
•• Обратиться к ReportLab API очень просто, достаточно передать response в качестве первого аргумента конструктору canvas.Canvas, который ожидает получить «файлоподобный» объект.
•• Дальнейшее создание содержимого документа осуществляется путем вызова методов объекта PDF (в данном случае p), а не response.
