Скорая Компьютерная Помощь г. Калуга

Полный спектр компьютерных услуг!

Здравствуйте, гость ( Вход | Регистрация )

> Внимание!

  • Вся информация, расположенная в данном и других разделах форума получена из открытых источников (интернет-ресурсы, средства массовой информации, печатные издания и т.п.) и/или добавлена самими пользователями. Администрация форума предоставляет его участникам площадку для общения / размещения файлов / статей и т.п. и не несет ответственности за содержание сообщений, а также за возможное нарушение авторских, смежных и каких-либо иных прав, которое может повлечь за собой информация, содержащаяся в сообщениях.
Ремонт компьютеров в калуге Рекламное место сдается
 
Ответить в эту темуОткрыть новую тему
> Google App Engine / Оптимизация HTTP-сервера через версионность ресурсов. Особенности реализации
Decker
сообщение 11.8.2011, 8:21
Сообщение #1


Администратор
*****

Группа: Главные администраторы
Сообщений: 14349
Регистрация: 12.10.2007
Из: Twilight Zone
Пользователь №: 1



  1. Суть оптимизации
  2. Page load vs forced refresh
  3. Потребуется автоматизация
  4. Реализация серверной части
  5. Оптимизация серверной части
  6. Особенности google app engine
  7. Исходный код
  8. Резюме




Рассматривается пример реализации для Google App Engine / Python.






Суть оптимизации


Инженеры Yahoo в своей известной статье писали об интересной технике оптимизации обработки HTTP через версионность файлов. Суть её такова… Обычно в HTML пишут просто:



< img src="image.jpg" >



Заполучив однажды image.jpg в кэш, после браузер снова считывает HTML и снова обнаруживает там ссылку на ту же картинку. Понять обновилась ли она на сервере в общем случае браузер самостоятельно не может, так что ему приходится послать запрос на сервер.



Чтобы избежать лишнего запроса, можо указывать версию ресурса в его адресе, сделав адрес уникальным:



< img src="image.v250.jpg" >



Таким образом, браузер может быть уверен что файл версии №250 в будущем не поменяется, и №251 тоже; и если №250 есть в кэше, значит можно использовать его безо всяких вопросов к серверу. Придать браузеру эту уверенность помогут два HTTP-заголовка:



// Будте спокойны, картинка не обновится никогда
Expires: Fri, 30 Oct 2050 14:19:41 GMT
// и может хранится в кэше вечность
Cache-Control: max-age=12345678, public



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




Page Load vs. Forced Refresh


В текущем виде эта оптимизация работает для переходов по ссылкам и для Ctrl+L, Enter. Но если пользователь обновляет текущую страницу через F5, браузер забывает что для ресурсов было указано «более не беспокоить», и вот на сервер несутся «лишние» запросы, по одному на каждый ресурс. Такое поведение браузеров уже не изменишь, но то что можно и нужно сделать — это не отдавать каждый раз файлы по полной программе, но ввести дополнительную логику, по возможости стараясь отвечать «у меня ничего не изменилось, возьмите из своего кэша».



Когда браузер запрашивает «image.v250.jpg», то в случае если у него в кэше есть копия, браузер посылает заголовок «If-Modified-Since: Fri, 01 Jan 1990 00:00:00 GMT». Браузер пришедший за этой картинкой в первый раз такой хидер не отправляет. Соответственно, север должен первому говорить «ничего не изменилось», а второму честно отдать картинку. Конкретно в нашем случае дату можно не анализировать — важен сам факт наличия картинки в кэше, а картинка-то там верная (из-за версионности файлов и уникальных URL'ов).



Но просто так заголовок «If-Modified-Since» на сервер не придёт, даже если картинка лежит в кэше. Чтобы заставить браузер отправлять этот заголовок, в (хронологически) предыдущем ответе нужно было отдать заголовок «Last-Modified: Fri, 01 Jan 1990 00:00:00 GMT». На практике это всего лишь означает что этот заголовок сервер должен отдавать всегда. Можно отдавать честную дату последнего изменения файла, а можно указать любую дату в прошлом — эта же дата потом пойдёт обратно на сервер, а там она, как уже выяснилось, особого интереса не представляет.



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




Потребуется автоматизация


Техника неплохая, но расставлять версии файлов вручную на практике маловозможно. В GAE/django проблема решается через custom tags. В шаблоне пишется код:



< img src="{% static 'image.jpg' %}" >



преобразующийся в HTML:



< img src="/never-expire/12345678/image.jpg" >



И вот реализация такого тэга:



def static(path):
return StaticFilesInfo.get_versioned_resource_path(path)
register.simple_tag(static)




Реализация серверной части


В основном, данная оптимизация удобна для обработки статических файлов — картинок, css, javascript. Но App Engine обрабатывает файлы означенные как static сама (не очень эффективно) и не даст поменять заголовки HTTP. Поэтому в дополнение к стандартной директории «static» появляется ещё одна — «never-expire».



Сначала обработчик GET-запроса проверяет что запрашиваемая версия файла соответствует последней. Если не соответствует — перенаправляет на новый адрес, для порядку:



# Some previous version of resource requested - redirect to the right version
correct_path = StaticFilesInfo.get_resource_path(resource)
if self.request.path != correct_path:
self.redirect(correct_path)
return



Потом выставляет заголовки ответа:

— Content-Type согласно расширению файла

— Expires, Cache-Control, Last-Modified как уже было описано.



Если в запросе замечен заголовок If-Modified-Since, ничего не делаем и выставляем код 304 — ресурс не изменился. Иначе содержимое файла копируется в тело ответа:



if 'If-Modified-Since' in self.request.headers:
# This flag means the client has its own copy of the resource
# and we may not return it. We won't.
# Just set the response code to Not Changed.
self.response.set_status(304)
else:
time.sleep(1) # todo: just making resource loading process noticeable
abs_file = os.path.join(os.path.split(__file__)[0], WHERE_STATIC_FILES_ARE_STORED, resource)
transmit_file(abs_file, self.response.out)



Возможно, если БД в GAE побыстрее файловой системы, стоит при первом запросе файла копировать содержимое файла в базу и потом обращаться уже только туда. Вопрос для меня открытый.




Оптимизация серверной части


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



os.path.getmtime(file)



Однако опрашивать файловую систему на каждый запрос вроде бы не очень хорошо — I/O всегда медленный. Поэтому можно собрать информацию о текущих версиях (всех) статических файлах при первом запросе и положить информацию в memcache. На выходе получается такой хэш:



{ 'cover.jpg': 123456, 'style.css': 234567 }



который и будет использоваться в custom tag'е для нахождения последней версии. Естественно, понадобится что-то вроде синглтона на случай если memcache протухнет:




class StaticFilesInfo():
@classmethod
def __get_static_files_info(cls):
info = memcache.get(cls.__name__)
if info is None:
info = cls.__grab_info()
time = MEMCACHE_TIME_PRODUCTION if is_production() else MEMCACHE_TIME_DEV_SERVER
memcache.set(cls.__name__, info, time)
return info
@classmethod
def __grab_info(cls):
"""
Obtain info about all files in managed 'static' directory.
This is done rarely.
"""
dir = os.path.join(os.path.split(__file__)[0], WHERE_STATIC_FILES_ARE_STORED)
hash = {}
for file in os.listdir(dir):
abs_file = os.path.join(dir, file)
hash[file] = int(os.path.getmtime(abs_file))
return hash





Особенности Google App Engine


Можно собрать информацию о всех статических файлах, но что если дизайнер изменит картинку? Как сервер узнает что пора обновить закэшированные версии файлов? В общем случае не очень себе представляю — нужно или заводить демона слушающего изменения файловой системы, или не забывать выполнять скрипты после деплоя.



Но App Engine — случай особенный. В этой системе разработка ведётся на локальной машине, после чего готовый код (и статические файлы) разворачиваются (деплоятся) на сервер. И, что важно, файлы на сервере уже не могут быть изменены (до следующего деплоя). То есть достаточно прочитать версии лишь однажды и более не заботиться о том что они могут поменяться.



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




Исходный код законченного примера


code.google.com/p/investigations/source/browse/#svn%2Ftrunk%2Fnever-expire-http-resources



svn checkout investigations.googlecode.com/svn/trunk/never-expire-http-resources investigations




Резюме


На appspot пока что не заливал, но локально всё работает и летает. Люди, пользуйтесь благами клиентско-серверной оптимизации, не отвечайте тупо 200 OK smile.gif
Original source: habrahabr.ru (comments).

Читать дальше


--------------------

Вернуться в начало страницы
 
+Ответить с цитированием данного сообщения

Ответить в эту темуОткрыть новую тему
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 

Рекламное место сдается Рекламное место сдается
Текстовая версия Сейчас: 18.5.2024, 21:54
Рейтинг@Mail.ru
Яндекс.Метрика Яндекс цитирования