Photo: Swamp under snow

Uncategorized Comments Off

Нога в снегоступе сминает сугроб,
Нетронутая целина впереди.
Воздух пахнет весной.



Share

Work: REST Batching, part III

Work Comments Off

продолжение см часть II “Идеальное” решение

Плохое решение

Multipart/mixed MIME messages.

Так как HTTP протокол полон приятных сюрпризов, то есть ещё один способ втиснуть несколько request-ов в один, и соотвественно несколько respons-ов тоже, в один. Всё, что надо сделать – это указать “Content-Type: multipart/mixed”. А затем аккуратно перечислить все что надо. Разделительные символы прилагаются.
Вот пример

POST /batch-proxy HTTP/1.1
Host: example.org
Content-Type: multipart/mixed; boundary=batch

-batch
Batch-Operation: POST /my/resource1
Host: example.org
Content-Type: application/xml
<?xml version="1.0″?>
<entry xmlns="...">...</entry>

-batch
Batch-Operation: DELETE /my/resource2
Host: example.org
If-Match: "ABC123XYZ"

Выглядит замечательно, не так ли? Снимается проблема ~37% overhead в трафике, так как можно текст передавать как текст, а двоичные данные как двоичные данные. Правда на этом достоинства и кончаются. А проблемы всё теже самые, и не REST и security hole, и не прозрачность в общем, смотри выше по списку.

Кроме того, надо помнить, что MIME был создан для передачи 8-битного текста через 7-ми битный SMTP. Да в нём есть много интересного, но HTTP не является MIME совместимым протоколом. Есть тонкие различия вызванные в основном тем, что HTTP оптимизировался для передачи данных через двоичные соединения + обратная совместимость, а у MIME были совсем другие проблемы – вроде ограничения на максимальную длинну строки в e-mail. Всех желающих углубится в эти различия приглашаю ознакомится с секцией 19.4 RFC2616

На практике всё это означает, что клиенту и серверу нужно иметь качественный парсер MIME сообщений. Не просто продвинутый HTTP клиент, но и такой довольно экзотический парсер. По этому пути пошли ребята из Microsoft построив свою ADO.NET Data Services Framework а также в Google – batching для GData. Если вам такой путь приемлем – то для Java есть бесплатный mime4j, а для .NET есть комерческий Mime4Net.

Для тех, кто не хочет возится с MIME есть уж совсем плохое решение

Совсем плохое решение

XML/JSON mark-up
Берем и решаем задачу в лоб. Конвертируем всё в текст, для разметки используем XML или JSON.
Пример с JSON

POST /batch-proxy HTTP/1.1
Content-Type: application/json
Accept: application/json
X-HTTP-Method-Override: BATCH
[
 {
   "method" : "PUT",
   "url" :  "http://someserver.com/some/resource/url",
   "body" : "<request body goes here>",
   "If-Match" : "xxxxxxxxxxx"
 },
 {
   "method" : "GET",
   "url" : "http://someserver.com/some/resource/url2"
 },
]

Пример с XML

POST /batch-proxy HTTP/1.1
Content-Type: application/xml
Accept: application/xml
X-HTTP-Method-Override: BATCH
<?xml version='1.0'?>
 <batch xmlns:b='http://batch.someserver.com/schema'>
  <b:request verb='put' uri= http://someserver.com/some/resource/url'>
   <b:headers>
    <b:header name='Content-Type' value='text/xml; charset=UTF-8' />
    <b:header name='Content-Length' value='XXX' />
   </b:headers >
  <b:body>
    <![CDATA[ ... ]]>
  </b:body>
 </b:request>
 <b:request verb='get' uri='http://someserver.com/some/resource/url2'>
  <b:headers>
   <b:header name='Accept' value='text/xml' />
  </b:headers >
 </b:request>
</batch>

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

Таким образом:

  • Каждая операция состоит из “конверта”, который содержит HTTP заголовки и тело запроса
    • “method” and “url” обозначают соотвественно HTTP verb и URL операции
    • “body”, обязательное только для POST и PUT, содержит данные которые были бы переданы в стандартом теле HTTP request/response
  • Произвольные HTTP заголовки тоже могут быть указаны как в request так и в response

А вот так например может выглядять response содержащий двоичные данные

200 OK
Content-Type: application/json; charset=UTF-8'
[
 {
  "code" : 200,
  "Content-type": "application/octet-stream",
  "Content-transfer-encoding": "base64",
  "body": "PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R=="
 }
 {
  "code" : 200,
  "Content-type": "application/octet-stream",
  "Content-transfer-encoding": "base64",
  "body": "PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R=="
 }
]

Что же плохого в этом решении? А все из списка приведенного в самом начале статьи. Всё что ни возьми – всё и плохо, и не REST. И тем не менее этот подход является самым простым для реализации. А простотой не стоит пренебрегать.

Есть несколько приёмов которые помогут предложить пристойные ответы на список недостатков. Можно

  • запретить запросы ко внешним серверам
  • запретить рекурсивные запросы к самому себе
  • ограничить количество запросов в пакете
  • ограничить общее время выполнения всего пакета и высылать на клиент HTTP error code 206 – Partial Content, в случае превышения
  • прекращать выполнение всего пакета после первой же ошибки
  • использовать протокол HTTP 1.0 при исполнении запросов в пакете
  • декларативно отказаться от атомарности операций
  • декларативно отказаться от оптимизации порядка или распаралеливания выполнения запросов
  • принимать только операции типа GET
Share

Work: REST Batching, part II

Work 1 Comment »

продолжение см часть I Общие положения

«Идеальное» решение

HTTP 1.1 протокол изначально поддерживает приём-передачу нескольких request-response. Для этого нужны persisted connections, pipelining и chunking см ниже.

Клиент открывает соединение, пишет туда request-ы, читает respons-ы. Кроме очевидных требований о поддержке этих persisted connections, pipelining и chunking на сервере и клиенте, есть ещё одна неприятность -  протокол требует, что бы клиент выслал заново все свои запросы сделанные в рамках этого pipelined соединения если оно прервётся в середине сеанса. А для того, что бы эту перепосылку можно было сделать без опаски, все запросы должны быть idempotent, т.е. только GET, HEAD, OPTIONS, PUT и DELETE. Это решение действительно очень хорошее с точки зрения REST – URI уникально адресуют ресурсы, HTTP headers означают правильные вещи и обрабатываются правильным образом, все транзитные сервера видят HTTP метод и могут, что-то правильное по этому поводу предпринять.

Однако, это «идеальное» решение существует главным образом на бумаге. Persisted connections не поддерживают мобильные устройства. Не все HTTP клиенты умеют читать chunked responses и использовать pipelining, привет AJAX рещениям. Да что chunked responses – для многих библиотек послать PUT запрос – уже проблема. Финальным аккордом тут является отсутствие поддержки метода POST в HTTP pipelining.

Раз «идеальное» решение не подходит, вернёмся к идее тунелирования HTTP протокола внутри HTTP.

Что такое persisted connections, pipelining и chunking:

Persisted connections

По умолчанию все соеднинения в HTTP 1.1 постоянные. Сервер не закрывает соединение сразу после обработки запроса тем самым позволяя клиенту использовать это соединение опять и опять. Если клиент желает получить несколько ресурсов с одного и тогоже сервера получается большой выигрыш в производительности. Вместо того, что бы открывать несколько соединений, все запросы пройдут по этому единственному каналу. Как клиент так и сервер могут оборвать этот канал с помощью HTTP header «Connection: close». Интересно, что запросы-ответы не обязаны быть строго последовательными, другими словами, клиент не обязан ждать ответа на первый запрос, а может сразу делать следующий и следующий, это становится возможным благодаря

Pipelining

Клиент посылает серию запросов, а сервер возвращает ответы в том порядке в котором были получены запросы. Часто бывает, что содержимое ответа генерируется динамически и сервер не знает точную длину которую следовало бы поместить в HTTP header Content-Length. Это нормально, в HTTP 1.1 в отличие от HTTP 1.0 заголовок Content-Length не является обязательным.

Так как Content-Length не передаётся, то нужен какой-то механизм, который бы сообщил клиенту, где кончается один response и начинается следующий. HTTP 1.1 решает эту проблему с помощью

Chunking

В случае динамических ресурсов, когда response не содержит Content-Length, он содержит Transfer-Encoding: chunked. Само же тело содержит куски с указаннием длины индивидуального куска. Кусок нулевой длины отмечает конец respons-а. Тут, пользуясь случаем, передаю привет IE6, который виснет, если ему этот последний кусок не передать.

Зачем я это всё рассказываю – в большинстве случаев, все эти технические детали скрыты в HTTP библиотеках или даже в абстракциях самого языка, но если мы уж собрались строить HTTP внутри HTTP то это надо понимать.

Что же делать? К сожалению существует только плохое решение и очень плохое.

продолжение см часть III “Плохие” решения

Share

Work: REST Batching, part I

Work 2 Comments »

HTTP внутри HTTP это, как реторта с
личинками дельфинов – вещь в себе

Если вы разрабатываете REST API, то рано или поздно к вам придут ваши клиенты с просьбой сделать пакетную обработку. Сделать так, что бы сервер приимал произвольный набор запросов одним пакетом и отправлял назад все результаты тоже одновременно. Это касается не только GET запросов, а любых методов – PUT, POST, DELETE и т.д. Ответы тоже будут разные, и будут содержать различные типы данных – например текст в различных encoding-ах и charset-ах, или двоичные данные в произвольном формате. В общем случае, скажут они, было бы полезно уметь обрабатывать пакеты любых HTTP запросов, в том числе запросы к внешним серверам которые необходимо исполнить последовательно с запросами к серверам из локальной сети.

С первого взгляда это кажется замечательной идеей. Клиент делает единственный POST запрос, получает единственный ответ и здорово экономит на сетевых задержках. На самом деле, здесь столько подводных камней, что вы сможете пожалеть что вообще с этим связались. Но выбора обычно нет. Что же тут плохого? Посмотрим сначала на

Религиозные проблемы REST

  • Очевидно, что URI этого прокси одинаков для пакетов любых запросов – на вход поступауют произвольные запросы, на выходе мы имеет произвольные ресурсы. Значит этот URI перестаёт уникально идентифицировать ресурсы которые этот прокси обрабатывает
  • Требуемая операция содержится не в HTTP verb, а в теле запроса. И несмотря на то, что POST request вроде как является non-idempotent & non-safe – внутри может быть что угодно – как набор совершенно безобидных GET запросов, так и действительно non-idempotent POST запросы, а может быть и вовсе некоторая смесь из них. Только клиент и сервет знают, что же там такое внутри. HTTP метод перестал быть понятным для всех транзитных серверов, таких как HTTP прокси и кеши
  • В зависимости от набора запросов в пакете, сервер может создать новые ресурс(ы), обновить или удалить старые, или просто вернуть запрошенный ресурс. Некоторые из этих запросов могли завершиться с ошибкой, а другие были успешными. Передать эту информацию обратно клиенту в HTTP header нелегко, значит она будет втиснута в body ответа. Значит response тоже перестал быть прозрачным для всех транзитных серверов

Ну вот и всё, что бы из REST сделать SOAP больше ничего не нужно. На самом деле, стало даже хуже, чем если бы SOAP использовалось с самого начала – транзитные сервера теперь не знают у каких request/response HTTP headers значат то, что написанно в спецификации, а у каких уже ничего не значат.

Существуют также и

Прочие недостатки пакетирования запросов

Каким бы образом это решение не было бы сделано, результат будет являться тунелированием HTTP протокола внутри HTTP протокола. Задумайтесь на секунду, что это значит.

  • Клиенты должны понимать все тонкости HTTP, и не только на этапе создания пакета запросов, но также они должны иметь обработчики ошибок, повторов и т.п. Они должны уметь обрабатывать все фокусы HTTP протокола – chunked и gzipped ответы. Перенаправления и HTTP headers
  • Транзитные сервера перестанут кешировать, исчезнет поддержка ETag, If-Modified-Since и т.п. А умные load-balancer-ы перестанут load balance-ить
  • Сломается “conversation pattern” если таковой будет подразумеваться конечным сервером.
  • Огромная дыра в security, привет системным администраторам
  • ~37% overhead в трафике из-за преобразования двоичных данных в текст, а также неизвестный CPU overhead на сервере и клиенте что бы эти данные кодировать-декодировать
  • Поддержка атомарности выполнения всего набора запросов
  • Определение порядка выполнения запросов в наборе

На самом деле, если заглянуть в спецификацию протокола HTTP 1.1 то можно увидеть, что существует «Идеальное» решение


продолжение см часть II “Идеальное” решение

Share

Life: Про бухих французов в Нижней Канаде

Uncategorized Comments Off

В художественной галерее AGO выставлены картины замечательного канадского художника Cornelius Krieghoff. Впрочем, канадским его назвать можно с большой натяжкой. Родился в Германии, жил в Нью-Йорке. Записался а армию США и дезертировал четыре года спустя. Удрал в Канаду, жил и писал в  Монреале, Торонто, Квебеке, Париже. Затем, на старости лет, почему-то переехал в Чикаго.

Но любим мы его не только за это. Любим мы его за живописание быта тех дней. Художник не разменивался по мелочам и рисовал только про пьянки и про индейцев. Строго говоря он писал только четыре типа картин:

1. Перевернутые сани
2. Бухие французы
3. Охотники с добычей
4. Индейцы на привале

Krieghoff, конечно, был настоящим мастером, вариаций этих четырёх тем он наплодил изрядно.

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

Иногда, видимо для усиления экспрессии, тема пьянки получает дальнейшее развитие. Вот на этом полотне под названием “На утро после пьянки” изображены гонцы за бухлом в перевёрнутых санях а также ещё с десятка два людей, продолжающих веселиться.

Каждый человечек отлично прописан во всех деталях, включая емкости с алкоголем.

А вот, извольте, французы напились и не заплатили за платную дорогу. Мальчик кричит им вслед непечатные слова, а инвалид смотритель даже порывается догнать.

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

Интересно, картины Krieghoff были настолько популярны, что раскупались как горячие пирожки, а в 19-том веке даже был черный рынок поддельных картин. Быть может это объясняет однообразность сюжетов. Официальная критика объясняет тематику нарушения закона – буйным духом художника.

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

Одна эта экспозиция стоит похода в AGO.

Share

Life: Они наступают

Uncategorized Comments Off

У меня на столе стоит вот такой вот телефон.

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

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

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

Share

Life: Календарь программиста

Life Comments Off

Календарь программиста на 2009 год

Share

Life: Про танки

Uncategorized Comments Off

Случайно наткнулся на статью про телеуправляемые танкетки времен второй мировой, и вспомнил где я их видел – в игре “Company of Heroes”! Я ещё очень хорошо помню, как удивился этому чуду инженерной мысли – маленькое, с гусеницами, едет быстро, кто это и откуда?, и пока я сидел и удивлялся, танкетка доехала до моих войск и рванула. Вот примерно так оно тогда выглядело

Get the Flash Player to see this content.

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

Get the Flash Player to see this content.

Read the rest of this entry »

Share

Life: Успехи wallpaper-строения

Life Comments Off


mandolux.com
Оказывается индустрия wallpaper-строения идёт в ногу со временем. В ответ на появление рабочих мест с несколькими мониторами они предлагают картинки специально нарезанные на части. И даже для 3-х мониторов им есть что предложить.

Share

Work: Google visualization and chart API

Uncategorized 1 Comment »

http://chart.apis.google.com/chart?cht=p3&chd=s:Uf9a&chs=250x100&chl=January|February|March|April

В свете инструментации кода, мониторинга и сбора статистики не смог пройти мимо довольно интересных средств визуализации. Нельзя сказать, что такого раньше совсем не было, можно только сказать – что такого качество, даже за деньги, ещё поискать надо. Итак, о Google Chart API,  Google visualization API и о том, как сложно сделать простую вещь.

Read the rest of this entry »

Share
Entries RSS Comments RSS Log in Admin