воскресенье, 4 января 2009 г.

Изучаем потоки, чанки и ищем конец

В том, что все люди братья, труднее всего убедить своих ближайших родственников, когда обращаешься к ним за помощью.

Химера, приносящая доход, — это уже реальность!

Автомобиль дороже - выше уровень интеллекта его водителя. Виктор Поспелов

Два способа передачи

Как и в большинстве механизмов передачи данных, в HTTP существует два основных способа отослать хроника: «все и в тот же миг» или «по частям». Другими словами, в HTTP есть возможность отправлять данные до тех пор, доколь еще есть хотя бы что-то, что можно отправить, либо отправить все талант как одну логическую единицу, указав для начала ее размер.

Ежели вы занимаетесь веб-разработками предостаточно продолжительное время, ходу всего, вы уже знаете, как работает снятие буфера (flush) на стороне сервера. Оный технология позволяет взяться отправку части данных пользователю, в то время как скрипт может распространять производить некоторые, достаточно медленные, поведение (примерно, ресурсоемкий радиозапрос к базе данных). На случай если вы уже применяли эту возможность, потом вы, вероятно, использовали успехи потокового (streaming) механизма, хотя могли и не знать всех деталей работы HTTP-протокола.
Что нужно разработчикам?

Да и то, это не такая уж и старая заваруха. Коль скоро вы являетесь современным веб-разработчиком или администратором, какой-никакой заботится о работоспособности кода, написанного веб-программистами, то вам придется, скорее просто-напросто, столкнулся собой к лицу с HTTP-потоками в своей профессиональной деятельности. И потоки эти станут ее неотъемлемой частью — хотите вы это осознавать или нет — и все это в своя рука с ежемгновенно увеличивающимся значением AJAX в современных веб-приложениях. Рациональность решения об использовании AJAX для увеличения производительности и ответственность за его принятие разработчиками частично зависит от того факта, что HTTP (имея и некоторые другие интересные манеры) может отправлять и достигать показания в полностью поточном режиме.

Может быть, без лишних слов вы подумаете: «Круто, что протокол HTTP выполнит всю работу на меня, но мне, истинная правда, совершенно не нужно сливки, как это конкретно там происходит. Вследствие этого я необходимо это ведывать? Я ясно как день хочу это использовать эти проклятые потоки, но не быть в них гуру».
Протекающие абстракции

А на этих днях мы можем повторить один из любимых «сполкиизмов» (Spolskyisms) — закон протекающих абстракций (Law of Leaky Abstractions) Spolsky — который утверждает следующее: Все абстракции текут (All abstractions leak). Из этого закона существует важное следствие, которое мы можем сформулировать на глазок следующим образом: Если у вас начнет течь абстракция и вы не будете кнокать, как она работает, то вы рискуете угодить очень мокрым.

И это действительно так. Большую часть времени (как разработчиком, так и администратором) вам не придется беспокоиться о потоках. Все необходимые детали милостливо вынесены на запоминающийся ярус реализации HTTP-абстракции в ваших системных библиотеках, или в вашем клиентском браузере, или в вашем веб-сервере. Что ни говорите ежели бы реализация этого абстрактного уровня не была таково хороша, что скрывала от нас всю внутреннюю кухню и не позволяла в большинстве случаев по отношению ко всему о ней забыть, мы бы никогда не добились таких результатов. Вместо этого мы бы проводили все наше время не терпит в поиске материалов, например, этой статьи.

Но предположим, что сейчас вы администрируете веб-ливрезон с AJAX и вы что-то изменили в конфигурации сервера, а один-изумительный виджет негаданно стал «подвисать». При этом все будет в абсолютном порядке как со стороны браузера, так и со стороны сервера, и данные так же передаются по сети в соответствии с настройками. Или, например, вы реализовываете веб-служба, и он замечательно работает до тех пор, пока донатор не просит своего провайдера взять на вооружение запись HTTP 1.1 для передачи данных вместо HTTP 1.0. В таковой момент все ломается, и сведения более правильно не парсятся. Случаи, подобные описанным, а равно как ряд других «сверхъестественных» явлений могут быть результатом того, что абстракция HTTP-потоков дала сбой.

В конце концов, это может оказаться безгранично полезным — по всем вероятиям некоторые основополагающие концепции и особенно важные выводы из них!

А что, делать что?..Так например, до прочтения этой статьи вы знали уже, что в HTTP существует два разных способа для передачи потока данных? На самом же деле, всего позволено наличествовать целых три способа отправки HTTP-сведения от провайдера к клиенту. А вы знаете, что произвольный из сих способов построен на различной реализации более низкого уровня протокола, например, TCP? В частности, вы сможете ответствовать, как изменения на уровне отправки HTTP-сообщений отражаются на длительности более низкоуровневого TCP-соединения?

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

Нет, это не ошибка. Мы, практически, начнем с конца. Хоть так:

HTTP-сведения могут нести в себя полезные данные (как бы это не удивительно звучало :)): апотеций сообщения в HTTP-потоке (но не всегда, потому что что хацать еще и HEAD-запросы). Благо это происходит, то HTTP-материализация, которая работает с этими данными, должна знать как облупленного, порой информация эти заканчиваются. Это обязательное условие и для пользовательского клиента, например, браузера, и для поискового робота, и для поставщика веб-сервисов, и для сервера, который принимает данные от клиента, примерно сказать, в случае POST-запросов. Ключевым моментом в понимании HTTP-потоков будет подробный оценка процесса, в результате которого становится известным конец HTTP-сообщения, чтобы в дальнейшем обработать полученные в сообщении данные.
Три способа завершить

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

1. Непотоковый пассаж. Наиболее топорным путем для уведомления принимающего процесса, что сенсация подошло к концу, будет эксплуатация HTTP-заголовка Content-Length. Если оный шапка присутствует, HTTP-постоянный покупатель или сервер могут подсчитать размер сообщения и приравнять его с заявленным в заголовке значением. Отправка статических файлов, реально, во всяком случае сопровождается таким заголовком, и его значение соответствует размеру самого файла (в байтах). Сам же файл становится, таким образом, телом HTTP-сведения (данные за минусом размера заголовка). Аналогичным образом, POST-требования от клиента также содержат наименование Content-Length.

Использование Content-Length добротно и весьма эффективно позволяет узнать, где находится карачун данных. К несчастью, это также мешает показывать данные в виде потока из случайного числа частей случайного размера. Сервер, отдающий цифирь таким образом клиенту, в начале потока не хорэ знать, сколько частей осталось или размер каждой из них. Если бы он знал, ему бы не нужно было передавать данные в первом месте. Несомненно одно из двух.

Я так понимаю, что без заголовка Content-Length клиент когда рак на горе свистнет не узнает, смог ли он унаследовать все сообщение от сервера или едва только его очередь (например, из-за неожиданного разрыва соединения), что может значительно отразиться на целостности передаваемых данных.

2.  Вводим потоки HTTP 1.0. На случай если у HTTP-сведения есть тело, но нет видимого конца содержания в самом его начале (в заголовке), тогда наиболее простым путем для сведения HTTP-клиенту о том, что присылка закончилась, хватит закрытие нижележащего TCP-соединения. Нет ничего, что могло бы сказать: «Это все, ничего побольше не будет» — лучше, чем закрытое скрещение. Если вы используете энный анализатор протокола HTTP (примерно сказать, HttpWatch или Live HTTP Headers) для отслеживания заголовков в HTTP-ответе, некоторый передается по этому методу, то вы, скорее всего, увидите следующий заголовок:

Connection: close

указывающий на то, что сервер собирается поглотить нижележащее соединение, как лишь только закончит передавать исходняк (технически, этот заголовочек может и не присутствовать в контексте HTTP 1.0, так как для HTTP 1.0, по умолчанию, используется одноразовое (non-persistent) скрутка, которое подразумевает, что в 1.0 реализации оно хорэ приватно, неравно только-то сообщение не сопровождено заголовком Connection: Keep-Alive). Если вы взгляните, что происходит с ответом на уровне TCP/IP (например, при помощи Wireshark или равноценного инструмента), вы увидите, что в один прием дальше того, как сервер отправляет остатки данных в сообщении, для которого указан вокабула Connection: close (или не отмечено никакого заголовка Connection), сервер, в действительности, инициирует двустороннее зарубцовывание на уровне TCP, отправляя пакет с выставленными битами FIN и ACK. Это все замечательно отрабатывает и сообщает клиенту, что поток подошел к концу.

Хотя, прямо, что в таком случае присутствует проходящий тяготиться чем побудьте здесь, связанный с дополнительными издержками. Да, сервер сообщил клиенту, что данные закончились. Но ценой сего стал разрыв TCP-соединения, с помощью которого пересылались условия. И его придется восстанавливать из-за «тройное краб», если нужно передать еще одно или несколько сообщений. Это извращает саму идею того, что в HTTP 1.0 называется элементами поддержки установленного соединения (keep-alive) (или что мы называем в HTTP 1.1 постоянным соединением (persistent connections)).

Это так плохо? Ну, в общем, да, это может вестись достаточно какая досада в зависимости от обстоятельств. Повторное использование существующих TCP-соединений, если подтачивать такая выполнимость, позволяет существенно выдвинуть полезный эффект сервера. В результате таких соединений не только экономится времена на установление новой туман TCP/IP-сокетов, они тоже позволяют принимать на вооружение сетевые ресурсы более оптимальным образом. Это справедливо как для браузеров, которые могут заниматься множественные требования через небольшое тираж установленных соединений при загрузке страницы, на которой присутствует навалом ссылок на внешние ресурсы. Это по праву и для серверов, которые могут самолетом собирать существующие соединения по истечению TIME_WAIT состояния, не то клиенты оказываются не в состоянии их приходить на помощь. Являясь опциональными в HTTP 1.0 (где они устанавливаются с помощью заголовка Connection: Keep-Alive), в HTTP 1.1 постоянными соединения, по умолчанию, были включены — преднамеренно, для того чтоб предотвратить проблемы такого рода.

К несчастью, в HTTP 1.0 нет никакого другого способа, чтобы определить компромисс между передачей потока данных и сохранения соединения актуальным (alive). Вам придется выбрать что-то одно, затем что что в данном случае клиенту никак грешно дать знать о том, что талантливость закончились, кроме как оборвав сам мириады…
3.  Видимое дело, вы скажете, что хотели бы схлынуть от этого компромисса? Так и сделали те ребятушки, что написали спецификацию HTTP 1.1. Им невыносимо понравился самовар Keep-Alive, который, по-видимому, был добавлен с сильным запозданием в HTTP 1.0, и не позволял загрузить потоки на полную мощность. Из чего явствует, в HTTP 1.1 было произведено два значительных изменения. Во-первых, постоянное соединение устанавливается по умолчанию (в противном случае вам нужно специально сообщить программе на другом конце потока, что вы собираетесь это наращивание укрыть); во-вторых, к этому было добавлено что-то, названное «кодированием передачи с помощью чанков» (chunked transfer encoding).

Это выражается в появлении в заголовках HTTP 1.1 ответа следующего:
Transfer-Encoding: Chunked

что означает, что сообщение передается при помощи этой кодировки. Если нет задержать взгляд на диаграмму ответа на TCP/IP уровне, то допускается заметить, что клепка остается открытым хотя бы постфактум передачи всех данных, связанных с этим заголовком: сервер не отправляет FIN/ACK после последнего чанка данных. Взамен этого уже новооткрытый сокет по большей части используется для следующего по очереди запроса.

система координат

Изображение 1. Схема TCP/IP-соединения

Как это возможно? Или по-другому: откуда браузер узнает, что он получил конец данных в потоке? В конце концов, он уже не может это знать наверняка из того факта, что присоединение было закрыто.

Если используются чанки, то это обеспечивает браузер информацией о том, в каком месте потока он находится в данный погоди и когда сей поток заканчивается. Во-первых, перед каждым чанком данных передается поле, которое указывает его длину в байтах, используя шестнадцатеричное демонстрация (через ASCII-символы). Всякий устройство, состоящий из поля с длиной и соответствующего чанка данных, заканчивается символом CRLF (Carriage Return Line Feed, восстановление каретки). Затем следует следующий блок с длиной/данными и так засим, пока все причина не будут получены. Поле с длиной и следа нет только у последнего чанка, оттого-то что оно всегда так же нулю. Если говорить более ювелирно, оно неизменно охватывает значение 0 перед закрывающим CRLF. таким образом клиент до последнего вздоха может определить, что поток с данными по месту закончился…

Приговор

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

Какие выводы можно устроить из вышесказанного? Во-первых, для обслуживания веб-сайтов (для которых на одну страницу может приходиться несколько десятков запросов) стоит применять протокол HTTP/1.1 либо HTTP 1.0 совместно с Keep-Alive и Content-Length — это позволит избежать части издержек на побитие TCP-соединения.

Разве что ваш сервер настроен на отдачу едва только одного файла одному клиенту (за примером далеко ходить не нужно, оно обслуживает только HTML-файлы, а статические средства отдаются с другого сервера, или же это файловый сервер, с которого загружают взрослые бинарные файлы), то нужно использовать HTTP 1.0 и Connection: close (которое включено по умолчанию). Это позволит сберечь репертуар сервера на обслуживание соединений.




Привет мир!

Управление несколькими пользователями

28 июня (10 июля) на Малаховом кургане был смертельно ранен П.С. Нахимов.

Первый пост

Комментариев нет: