Почему не нужно начинать с Django
пост написан и отправлен в печать 2011-08-28 примерно в 20:26
У меня случилось огромное "горе" - проект, который был взращен мной и небольшой компанией программистов практически для личного пользования, вдруг стал популярен, подвергся реддит, дигг и стамблапон эффектам (и пережил их почти без даунтаймов, эта отдельная гордость и тема для другого поста), обзавелся серьезными инвесторами и наполнился хорошей релевантной активной аудиторией. Обойдемся без имен и названий - нас интересует исключительно технологическая сторона. Случилось это около года назад, под управлением талантливого PMа всего через 5 месяцев после публичного старта удалось монетизировать сервис без потерь лояльности клиентов - правда пару злых мудаков в бложиках все же плюнули злым словцом в сторону нашей корыстности. И вот, в один прекрасный день мы уткнулись носом в стену. Данный пост поможет вам не повторять наши ошибки и призван поделиться опытом выбора инструментария разработки и деплоймента в самом начале долгого пути.

Основная проблема в проектах нашего типа - недальновидность, по этим же причинам facebook в свое время был написан на PHP, который в последствии пытались собирать в С++ в HipHop, не знаю,чем там все закончилось, но подобных примеров миллионы, к сожалению, лишь крупные компании на этапе первичного планирования могут позволить себе разработку архитектуры горизонтального и вертикального масштабирования и предвидеть дальнейшее развитие проекта. Все маленькие команды создают проекты на одной левой коленке с помощью простого и быстрого инструментария, а далее начинает действовать правило снежного кома - старый код обрастает новым и заменить ядро можно только полность раскопав верхние слои. Не обошла стороной эта проблема и нас, для реализации был выбран фреймворк Django, возможностей которого хватало примерно с год, а дальше мы столкнулись с проблемой большинства. При куче достоинств у этого инструмента есть один большущий минус - узколобое и совершенно не гибкое ORM на котором завязано абсолютно все: CRUD интерфейс панели администрирования, модуль аутентификации, генератор форм и тому подобное. Проблема была достаточно серьезная - нужно было либо внедрять прямые SQL инъекции, используя сторонние raw sql билдеры, сведя на нет переносимость, абстрагированность от синтаксиса и саму идею ORM, либо искать альтернативные решения. Второй вариант показался нам более предпочтительным, принимая во внимание тот факт, что из Django мы использовали лишь верхний слой шаблонизатора, URL Routing, ORM (ну и генератор форм) и memcached backend. Для шаблонов сразу был найден jinja2, практически полностью совместимый с django-templates, небольшие синтаксические различия состояли лишь в передаче параметров фильтрам и отсутствии некоторых методов (вроде того же {% url %}, он повсеместно использовался нами для генерации ссылок), которые были написаны самостоятельно с учетом особенностей нового ядра. Вторая часть команды занималась портированием контроллеров в новый фреймворк Pyramid (продолжатель идей Pylons), а мне оставалась самая "вкусная" работа - переписывать модели с Django ORM на SQLAlchemy, ну и переносить данные. Не буду углубляться в детали, скажу лишь, что ничего не мешало использовать алхимию поверх таблиц, генерированных джанго - в отличии от своего инвалидного собрата, sqlalchemy умеет custom database scheme, но хотелось абстрагироваться от ужасных many-to-many relations с таблицей в 500K записей, оставленных в наследство. Проблема генерации и валидации форм тоже была сведена на нет с помощью formalchemy. В остальном, это была вполне выполнимая рутина. Через 10 дней в SVN ветке с заветным названием migration уже была рабочая тестовая версия, которую нужно было скармливать обезьянкам из отдела тестировки и QA.

Теперь поговорим про deployment. В датацентре компания арендует несколько серверов в стойке - фронтэнд с nginx для простой пропорционального upstream load-balancing, 2 backend-а для python и memcache, 3 сервера для pgpool-II PostgreSQL кластера. Статика и файлы пользователей хранятся в Amazon S3. Раньше на бэкэндах мы использовали FastCGI интерфейс manage.py прямо из коробки Django руководствуясь приципом "нам лениво", с переходом на Pyramid был выбран родной для Python WSGI и Green Unicorn в качестве сервера, который умеет работать с Paste-совместимыми фреймворками. Для данного окружения был создан отдельный virtualenv на каждом сервере, позволяющий вести тестирование и установку новых версий необходимых библиотек, не боясь повредить хрупкие dependancies для уже работающего проекта. В качестве сторожевого пса была выбрана тоже не чужая для python система контроля процесса supervisord. Для управления нашими новыми окружениями было построено небольшое приложение на основе fabric, инициирующее svn up обновление bleeding edge версий через SVN post-commit хук и перезагружающее gunicorn workers простой и удобной коммандой kill -HUP `cat /var/run/g_be.pid`. 

Предварительные выводы пока делать достаточно рано - новая версия все еще не ушла в production, но синтетические нагрузки с помощью утилит аля siege показали заметную экономию на времени генерации страницы и ресурсах CPU\RAM. Что касаемо Django - поистине великолепный инструмент, Ruby On Rails, но только для Python и я ни в коем случае не списываю его со счетов. Этот блог был целиком и полностью написан на Django за 5 часов, но если вы планируете создать что-то, что гипотетически будет усложняться структурно - вам необходимо смотреть в сторону альтернативных инструментов. Удачный пример миграции, описанный в этом посте - скорее success story и большая удача, чем правило. Просто большинство нашего функционала было вынесено в packages, а Django был просто прослойкой для создания логики. Вам же может повезти намного меньше.

И в качестве послесловия: опытный программист и системный архитектор (охуитительно же звучит) в лице меня ищет интересную работу на расстоянии. Интересную не в смысле денежной компенсации или комплексных обедов, интересным и технически сложным должен быть проект и команда. Хотя команда может быть и не сложной, просто опытной и приятной в общении. Рассмотрю любые предложения, отправленные мне на электронную почту, указанную в шапке.
NodeJS + SocketIO + Linux = сервер генерации скриншотов сайтов
пост написан и отправлен в печать 2011-07-29 примерно в 17:10
Сегодня мы поговорим о разработке сервиса создания скриншотов сайтов. Так получилось, что я занимался этим для одного интранетовского проекта и, получив благословение и разрешение добродушного заказчика из лесистой Финляндии c аватаркой чрезвычайной усатости, публикую тут подробное описание процесса и технологий, возможно, это кого-то заинтересует. Использовать мы будем следущий инструментарий: NodeJS, SocketIO, Xvnc, xwd, Chromium и еще кучу всего другого. Вкратце постановка задачи выглядела подобным образом: нужен API, возвращающий скриншот сайта в PNG, проект сам по себе - внутренний сервис компании юзабилити тестирования. Только нужно было управиться без всех этих хитрожопых и кривых command-line конверсий по типу khtml2png, а грабить настоящий живой вывод современных браузеров. Я, почесав репу, решил выделить три уровня разработки и автоматизации: serverside, middleware и конечно же clientside, которые я вам сейчас и опишу, дорогие мои.

Начнем с самого глубокого серверсайда. В распоряжении имелся старенький слабенький сервачок с ненавистным CentOS-ом, ну делать было нечего. Прежде всего, нужно понять, что без иксов нам абсолютно никак не обойтись. Они нужны для виртуальных дисплеев, куда мы будем высаживать наши браузеры. Варианта два: мы можем использовать xorg-x11-server-Xvfb либо же vncserver, который представляет собой perl обертку вокруг Xvnc. Я советую остановиться на втором варианте преимущественно по причине меньшего количества dependencies, возможности удобного удаленного подключения для настройки того же браузера и отсутствия проблем с битностью цвета. К тому же, на старике-центосе Xvfb почему-то постоянно отваливался, а ядро было собрано без поддержки framebuffer, поэтому устройство /dev/fb[0-9] отсутствовало, сводя на нет все преимущества утилит fbdump и fbgrab. После установки vncserver, протестируем его работоспособность, вызвав vncserver :11 -geometry 1920х1200 -depth 24. У вас спросят пароль при первом запуске и, если все прошло без ошибок, то вы счастливчик. Иксы на виртуальном дисплее localhost:11 у вас уже есть. Можете рассказать это своему environment (ненавижу слово "окружение") c помощью export DISPLAY=:11. А еще вы можете запустить xterm и сконектиться через VNC клиент (для виндовса есть отличный бесплатный от RealVNC) и в очередной раз убедиться, что все работает. Теперь нам нужно как-то воровать экран и сохранять его в изображение. В интернетах советуют использовать утилиту import, входящую в набор ImageMagic, но это как перевозить две горошины на грузовом самолете. Вместо нее мы будем использовать xwd из xorg-x11-apps для снятия слепка с виртуального дисплея, и xwdtopnm плюс pnmtopng из netpbm-progs для конвертации его в PNG формат. Тут все еще проще, для получения скриншота вам нужно просто выполнить xwd -root -display localhost:11 | xwdtopnm 2>/dev/null | pnmtopng > screenshot.png. Большая часть серверной магии окончена, осталось сделать небольшой скрипт автоматизации и защиты от дураков. Оговорюсь сразу, что процесс генерации сриншотов будет последователен на основе очереди задач, никакого распараллеливания. О причинах этого мы поговорим чуточку позже. После установки Хромиума, что для ебучего CentOS-а, который сыпется пылью из всех щелей, тоже нихуя не тривиальная задача, мы сделаем небольшой скрипт для облегчения рутины создания скриншотов. Хромиум запускается в инкогнито режиме в фоне, мы ждем открытия страницы 6 секунд (для многих страниц этого времени не хватает, но для тестов сойдет), снимаем скришот и жестко тушим все открытые процессы. Если вам не нужны элементы оформления браузера аля адресная строка или панель табов, то можете добавить ключ --kiosk при запуске хрома. Все пропадет, останется лишь окно с отрендериной страницей, но это выглядит как-то менее эстетично ;). Таким образом, мы собрали все необходимое для создания скриншотов, осталось написать обертку и обертку над оберткой. Пару слов про безопасность: создайте отдельного пользователя из-под которого будете запускать браузер и иксы, отключите все плагины на внутренней странице about:plugins. Клиентская и серверная валидация ссылок в добавок к жесткому ограничению времени исполнения (у нас это 6 секунд) защищает от умников, которые мечтают о stack overflow и arbitrary code execution или пытаются банально загрузить html файл размером с пару гигабайт. От запуска нескольких инстансов браузера для многопоточной генерации пришлось отказаться по этой же самой причине. И да, все настройки хромиума хранятся в JSON формате в файле ~/.config/chromium/Default/Preferences, изменить вам придется параметры размеров окна, потому что даже с ключом --start-maximized у браузера развернуть окно на весь виртуальный экран почему-то не получается.

Часть вторая - middleware или, другими словами, прослойка между сервером-клиентом и своеобразный примитивный менеджер задач. Писать мы ее будем на NodeJS и SocketIO, оба решения мне полюбились event-based моделью. В стандартный набор ноде входит функция spawn объекта child_process для асинхронного запуска дочерних процессов и получения их stdout потоков, которая и будет работать с нашим небольшим bash скриптом. Для создания последовательной модели исполнения, нам нужен какой-то неблокирующий алгоритм task queue и именно поэтому нам не подходит метод Array.forEach. Я, признаться, не стал ебать себе мозг академическими решениями и просто сделал рекурсивную функцию, которая банально вызывала Array.push при добавлении новой задачи и Array.shift при завершении выполнения и переходе на новый цикл итерации с проверкой блокирующей переменной. Решение не идеальное и при больших нагрузках могут возникать проблемы с выпадающими из стека заданиями, но никто больших нагрузок и не ждет - идеальная отговорка для лентяя, который поленился сделать асинхронную модель обработки с помощью setInterval. Чтобы не быть голословным - вот вам код прослойки. Судя по коллекции сохраненных скриншотов, некоторые куллхакеры, думающие, что, выполнив в консоле isURL = function() {return true;}, они обойдут все проверки и удалят мне /etc/passwd, соснули хуйцов и посмотрели веселые картинки на сайте gay.ru. Кекеке!

Последний и самый верхний уровень - clientside не представляет собой ничего особо интересного. Хочу лишь заметить, что при всех преимуществах охуительнейшей библиотеки SocketIO, у нее нету нормальной внятной документации или хотя бы описания API. Лично мне найти не удалось, на гитхабе только описание в примерах, но это не удобно и противоречит законам мироздания. Справедливости ради скажу, что все, описанное мной в этом посте, пахнет влагой и сыростью. Нету ни обработок исключений, ни проверок результата генерации, ни нормальной валидации ссылок, но я лишь даю болванку, а ее обтачивание - дело дорчитателей, которым это необходимо.

Куда ж мы без живых примеров? Никто в наше время тексту не верит. Пришлось перенести все на свой старенький сервачок в далекой Фрицландии, сделать симпатичную обертку на скорую руку и теперь вы можете проверить работу теории на практике в разрастающейся секретной экспериментальной лаборатории интернетовских опытов имени Коли Цискаридзе. Ссылка на ScreenShooter, который старательной описывался в этой статье, работает до последнего посетителя. В качестве бонуса всем, кто дочитал достаточно сложную для формата блога статью, я дарю самодельный уникальный инструмент по раскрутке вашего сайта, известный подписчикам моего твиттера и гуглоплюса. А если серьезно, спасибо всем, кто активно плюсует гуглокнопкой мои посты. Это достаточно приятно и позволяет понять направление дальнейшего развития тематик. Продолжайте в том же духе, нажимайте +1, у меня еще много разнообразных интересностей в черновиках.

Продолжаем копать HTML5: Comet, WebSockets, EventSource, NodeJS
пост написан и отправлен в печать 2011-07-18 примерно в 08:01
Все помнят мой гневный (скорее, ироничный вентиляторно-лопаточный вброс, на который многие купились, смотри UPD внизу следующей ссылки) пост про HTML5 и про весь тот шум, который нагнетается вокруг сырой и неготовой к массовому использованию технологии. В какой-то мере, данный пост станет очередным тому доказательством, но, в то же время, если этот эфемерный набор стандартов будет когда-то окончен, то мы, вебразработчики, получим отличный инструментарий, который лишит необходимости собирать велосипеды с квадратными колесами для езды по стеклянным рельсам. Ну вы поняли.

Давайте по порядку. Сейчас в моде real-time коммуникации, в техническом смысле этого слова. Контакт, лицокнига, гуглоплюсы - все перечисленные социальные сети имею свой WebIM, позволяющий в реальном времени общаться со своими друзьями и подругами. Правда, концепция там немного другая, чаты этих социалок, как правило, представляют собой JavaScript обертку вокруг XMPP протокола, но смысл понятен. Еще, справедливости ради, стоит заметить, что WebSockets и Server-Sent Events в настоящее время выделены из спецификации HTML5 в отдельные доки, но, опять таки, я утверждаю и буду утверждать, что HTML5 - не технология, а маркетинговый бренд и писькомерялка современных браузеров, поэтому отождествление понятий тут допустимо. Передо мной была поставлена задача создать приложение, позволяющее в реальном времени обмениваться информацией между сервером и клиентом с минимальными задержками. Благо, приложение это исключительно интранетовское и платформа использование - Google Chrome, а значит технически я не был ограничен. Вариантов было несколько: long-polling HTTP запросы, HTTP Streaming или fast-polling, все это называется Comet моделью. И у каждой реализации есть свой недостаток. В спецификации HTTP/1.1 четко указано, что браузер не должен создавать одновременно более 2х параллельных соединений к серверу. Конечно, современные браузеры могут поддерживать десяток connections и подобное нарушение стандарта не является фатальным, но в каждом из вариантов решения есть свои недостатки. Fast-polling, который представляет собой многократное повторение запроса (с завершением сессии) создает приличную нагрузку на сервер, а "висящие" запросы - костыли, которые приводят либо к memory-leak (в последнем фаерфоксе сделать garbage collector для буфера запроса не удосужились, в хроме и сафари память чистится, но только для запросов, возвращающих "Transfer-Encoding: chunked"), к тому же, контролировать Comet сессию сложнее, она может отваливаться без какой-либо видимой причины, да и изначально протокол создавался не для этого. Есть еще BOSH, который используется в вышеупомянутом XMPP, но он заточен больше под создание чатов и готовых решений для этой технологии очень мало. В PaaS Google App Engine, на котором расположен этот блог, есть Channel API, что-то вроде Comet-а, я даже пытался попробовать, но возможности тоже сведены до минимума. Конечно можно еще использовать обертки вокруг flash socket или java-апплета, но не этому учила нас партия.

Вот именно таким путем, перепробовав все вышеназванные методы и оценив ресурсоемкость, я решил остановиться на server-sent events и web-sockets, которые вроде даже выведены из статуса эксперементальных в последних версиях Webkit-а. Начнем с SSE, спецификацию можно почитать тут. По сути, это тот же HTTP Streaming на стероидах. Его смысл заключается в том, что в JavaScript коде или DOM создается объект EventSource со скриптом в src атрибуте или методе-конструкторе. Этот скрипт, прежде всего, должен в контексте "висящего" соединения (хотя и не обязательно) возвращать корректный Content-Type: text/event-stream и в дальнейшем push-ить обновление клиенту в виде строки data: any_text_info \n\n. То бишь, протокол абсолютно односторонний, нас это вполне устраивает, общаться обратно мы можем и через милый сердцу XHR. Есть в нем и вкусности, к примеру, ответ event: test-remote-event \n data: success \n\n, если верить стандарту, можно обрабатывать в addEventListener('test-remote-event' ,function(data){}), правда, это пока только на бумаге. В Chrome Canary у меня работал только event onmessage. Со стороны все всегда кажется таким красивым, но тут начались проблемы, бывает, что поток просто висит. Никакой onerror или onclose не вызывался, сервер продолжает исправно посылать данные, что проверяется curl-ом в консоли, но браузеру пофиг, хотя рестарт страницы помогает. Пробовал в Chrome Stable - та же  заморочка. ЧЯДНТ? Вторая и более серьезная проблема - отсутствие поддержки CORS, Access-Control-Allow-Origin: * отсылается, но браузерам на него с большой колокольни, они возвращают DOM Exception 18. Очередная брешь в стандарте, как так - XHR работает, а EventSource нет. Конечно, это все придирки, можно было бы завернуть трафик через proxy_pass в nginx, но в любом случае, осадок остался. К тому же, нужно уже было думать о проверке наличия \n\n в контенте, иначе была бы вероятность получить побитый ответ. Таким вот путем я и пришел к WebSockets. Рассказывать что-либо про клиентскую часть не имеет смысла, есть спецификация, все это делается одной строкой кода, давайте лучше кратко поговорим, как реализовать подобное на сервере. В качестве server-side для подобных проектов я всегда рекомендую брать NodeJS. Сама парадигма event based неблокирующего асинхронного сервера идеально подходит для высокозагруженных API, а этот самый интранет, он очень большой и запросы ресурсоемкие. К тому же, имеющегося в NPM добра с головой хватит для решения любых задач.

Для демонстрации возможностей, я написал небольшое приложение. Прописано оно по адресу. Работать должно в Chrome, Safari (в том числе и на iPad, iPhone с 4.2) и FF. Но в последнем нужно включить поддержку сокетов. Вот вам мануал. Долго думал, откуда бы взять большой поток данных для наглядного примера, как вспомнил про Twitter Streaming API. Код готового приложения можно невозбранно скачать архивом. Если демонстрация перестанет работать - прошу меня простить, мой старенький сервер очень нежный и неторопливый, и больше времени заняла компиляция node, чем прочтение документации или написание самого кода. Разумеется, в server.js нужно вписать свои логин и пароль. Вообще, я использую sample поток, а вы, прочитав документацию по твиттер апи и twitter-node, можете создать какой-то полезный сервис мониторинга и аналитики хештегов, или выводить в форме границ Украины аватары пользователей, написавших что-то на мове. Фантазии ограничены исключительно возможностями API твиттера, а их более чем хватает.

В заключение, хочу сказать, что я постепенно пересматриваю формат блога, если вам понравилась подобная статья - не поленитесь перейти на веб версию, если в РСС читаете, и воспользоваться кнопкой Google +1. Если соберется немного плюсов, то в ближайшее время мы поговорим про WebWorkers, Forms Validation, нарисуем мужской половой хуй в Canvas, напишем вращающийся чайник на WebGL и попробуем накладывать фильтры и получать информацию из тега <video>.

Google Storage for Developers vs Amazon S3
пост написан и отправлен в печать 2011-04-22 примерно в 09:42
Прежде всего, хочу извиниться перед всеми, кто шлет мне слезные письма. Мол, Володенька, родненький, перестань писать непонятные посты, давай как раньше, чтоб за живое с кучей матов и смешно, и за жизнь чтоб, про политику, Тимошенко там или Обаму Хусейновича. Прочитаешь бывало, а потом детям покажешь и сам поржешь. Ну а сейчас с женой читаем и ничего не понимаем, зачем ты так с нами? Мы же твоя целевая аудитория. Так вот, аудитория, слушайте меня сюда, если кто мою Кристиночку. Я не ибигдан, не тема с двумя точками над буквой "е" и уж тем более не другой. Если кто не читал дисклеймер, то осмелюсь повторить его ключевую мысль здесь. Мне в хуй не упало, сколько людей меня читает, пишу я исключительно для себя, чего и вам желаю, солнышки вы мои. Вот такой вот я брутальный социопат, lmao.

А теперь перейдем непосредственно к теме сегодняшнего моего монолога. Google, не прошло и года как, прислал мне приглашение "на посмотреть" на облачную систему хранения больших объемов данных в массиве их content delivery network под названием Google Storage for Developers. Прислал очень неожиданно, всего через час после того, как я включил платный себе аккаунт на Google Apps Engine, это вам такой намек, хозяйке на заметку, зарегистрируйте свою карту в Google Checkout и инвайты приду сами собой, гугл не любит нищебродов, ага. Так вот, всем нам известен первопроходец, ставший практически монополистом в сфере cloud computing + storage. Amazon уже достаточно долгое время предоставляет за смешные деньги всем желающим возможность воспользоваться своим CDN для распространения контента по всему миру на больших скоростях. Тут нужно упомянуть о текущем бесспорном преимуществе S3 над GSfD, которое заключается в географической разветвленности датацентров амазона, гугл же пока дает место только на континенте демократии. Возможно, все изменится после выхода API из состояния бетатеста, все же у гугла серверных мощностей по всему миру будет побольше, чем 5 датацентров амазона. Вообще, такой вот фразой, про бетатест и раннюю стадию развития, можно оправдать отсутствие всего необходимого в Google Storage. Я думаю, что многие видели интерфейс AWS Management Console (кто нет - тут вам скриншоты), гугл в этом смысле немного отстает, предоставляя лишь базовый функционал создания ведра (мне нравится так переводить bucket, хотя, в принципе, корзина подходит много лучше), создания вложенных в ведро директорий, загрузки файлов и получения публичных ссылок на них. Все это выглядит у гугла подобным образом, и есть еще интерфейс управления ключами доступа. У Amazon все же лучше получилось, учитывая их расширенный copy-cut-paste функционал и выпадающее контекстное меню. Гугл не позволяет даже упорядочить файлы в ведре по их размеру или дате создания, что вообще печально. С другой стороны, тут есть любимый мной drag and drop, когда, перетянув файл в открытую вкладку GSfD, мы инициируем его загрузку. Не зря в свое время гугл очень суетился, проталкивая эту draggable file objects спецификацию в стандарт html, и использует ее сейчас практически во всех своих SaaS-ах. Недостатком является невозможность загрузки целой директории, путем ее перетаскивания, но это скорее security policy ограничение стандарта, а не интерфейса. Это что касается визуальных свистелок и перделок, теперь стоит сказать о том, что интересует developers, для которых, собственно, исходя из названия, и был сделан Google Storage, а именно - RESTful API сервиса. Для red-eye управления своим стореджем гугл сделал консольную утилиту под названием gsutil, которой можно сделать абсолютно любые операции над своими ведрами, но в то же время (вниманием), гугл не изобретала велосипед и сделала свой API абсолютно совместимым с API S3, а значит все утилиты требуют минимального фейслифта в виде смены ENDPOINT и xmlns для миграции с облаков на облака. Что касаемо модульной реализации для разных языков, то в качестве примера в Google выбрали boto - Python interface to Amazon Web Services c минимальным изменениями. Очень все это не похоже на гугл, ведь, по сути, даже сам gsutil это практически s3cmd. Скажу сразу, моя радость по поводу возможного объединения Google Apps Engine (все же blob storage c его глупым лимитом чтения 1MB per 1 API call заставляет писать неприятные ресурсоемкие велосипеды) и Google Storage for Developers была преждевременной, у URLFetch библиотеки - единственной "дырки", через которую можно выйти за пределы своего окружения в GAE, есть один неприятный лимит 1MB max upload and download per 1 API Call. Ах ты ж ебаный ты нахуй, не иначе. Все это, конечно, решаемо, но ценой процессорного времени, которое денежку стоит. Хотя, если посмотреть на Roadmap проекта, то гугловцы клянутся, что в ближайшем времени интеграция этих двух сервисов должна пройти в рамках GAE, искренне надеюсь, что без глупых лимитов. Что касается скоростей загрузки\отдачи, то мой опыт и обзоры в интернете показывают, что GSfD совсем немного быстрее, это вполне объяснимо менее активным его использованием. Так что и тут пальцем в небо. При всей моей любви к поисковому гиганту, я не могу найти хоть одно маломальское преимущество. Об этом мы и поговорим в завершении поста (тут могла бы быть шутка про Пасху, но нет).

Ну вот, раз мы теперь знаем, что у обеих компаний все одинаково, стоит спросить себя, а нафиг вообще гугл затеял эту возню и как он собирается переманивать клиентов? Правильным ответом могли бы стать цены, но нет. Сравните стоимость Amazon S3 (я уже молчу про их RRS) и Google Storage for Developers. И без калькулятора видно, что даже в самом дорогом датацентре цены у амазонки то пониже будут, я уже молчу про наличие одного бесплатного гигабайта out-трафика в качестве вкусной плюшки для мелких сайтов или экспоненты снижения стоимости bw при большом потреблении. Какие мотивы остаются у гугла для разработчиков? Только лишь географическая распределенность хранилища и тесная интеграция со своей вычислительной платформой Google Apps Engine. Ни первое, ни второе не реализовано, а есть только громкое имя, копипаст технологии, завышенные цены и пока еще не совсем определенное будущее. Будем надеяться, что гугл, как и в случае с Google Apps, оставит небольшую бесплатную квоту, которой будет хватать для личного, небольшого приложения, тем самым завлекая разработчиков на свой сервис. Других путей достижения успеха, а не создания очередного сервиса аля Google Buzz (много шумихи, драка за инвайты, обзор месяца на слешдоте и т.д., и т.п., но пользуются единицы), я не вижу.

Пишем свой caching nameserver с фильтрацией
пост написан и отправлен в печать 2011-04-09 примерно в 10:58
Прежде чем начинать свое повествование, маленькое сообщение для всех, кто прочитал топик поста и закачал убеленной сединой\лысиной головой с неприкрытым укором: да, я знаю о существовании BIND и да, я знаю про MySQL BIND SDB, или даже про MyDNS тоже слышал, и настроить кеширующий child неймсервер умею. Увы, реализация такой комбинации не виделась мне целесообразной по ряду причин о которых я не буду тут писать. В любом случае, мне нужно было сделать DNS с фильтрацией определенных доменных имен с хранением списка этих имен на центральном сервере в MySQL базе данных. Нужно было сделать его не намного медленней родного варианта, что было вполне нетривиальной задачей. Думаю, что для начала мне стоит объяснить, зачем это все задумывалось. Как упоминалось в прошлом посте, я работаю в компании основным направление деятельности которой является предоставление сервиса защищенного VPN туннеля для обхода корпоративных фаерволов, шифрования трафика, защиты от man-in-the-middle и прочих мерзких снифферов, получения своего личного реального IP где-то в странах экваториальной Америки, ну и для защиты от вирусов с помощью решений от тов. Касперского. В качестве серверных площадок используются разнообразные virtual dedicated и root dedicated от разных провайдеров по всему миру. И вот тут возникает основная проблема, которая загубила большинство подобных сервисов - торренты. Да, в большинстве TOS-ов большинства хостеров ясно прописано, что если с вашего сервера будет замечена подозрительная активность по загрузке из интернета нелегального контента, то вы автоматически лишаетесь предоставляемой услуги без какой-либо надежды на материальное возмещение. Звучит достаточно печально, учитывая предоплату на целый год. В итоге, часто получаешь подобные письма, рассылаемые полуавтоматическими системами правозащитных ищеек, что не добавляет хорошего настроения в твой и так не особо веселый рабочий день. Каким образом они отслеживают попытку скачивания торрента? Fake Seed к примеру? Или просто фильтруют запросы к торрент трекерам? Я без понятия, но вот если правильным образом не среагировать на подобное письмо, убедив хостера, что этот мудак, качающий Спанжбоба, физически устранен, а вся его семья стерилизована - можно получить серьезные проблемы. Вплоть даже до судебного разбирательства, все мы новости читаем. Это проблема, теперь поговорим о решении. Любой системный администратор скажет вам, что не существует оптимального решения перекрытия кислорода p2p трафику. Да, можно ограничивать количество udp сессий одного хоста (что достаточно глупое решение, учитывая саму природу протокола с неявным закрытием соединения), можно вообще закрыть udp (что еще глупее, одним махом лишаем пользователя всяких скайпов\сипов, а торрент клиенты могут спокойно и через TCP работать), можно использовать всякие модули фильтрации уровня iptables вроде ipp2p, OpenDPI, l7-filter (на сайте пишут про высокий perfomance, но у меня la вскакивал до 1 при 10 соединениях и иногда всплывали defunct зомби) или вообще пускать весь трафик OpenVPN  в лоб через фильтрующий прокси сервер (мною было опробовано все из списка, увы, все решения крайне требовательны к системным ресурсами из-за необходимости прочесывания регулярками большого потока данных и по этой причине не подошли, не все сервера в сети могли позволить себе такую роскошь). Есть еще решения для дураков: закрытие типичных портов, что не работает в условиях современных клиентов и тот вариант, который выбрал я - DNS блокировка главных торрент трекеров. Защита ужасная, ничто не мешает клиенту после подключения выставить вместо присваиваемого неймсервера какой-то другой, данное решение к тому же не помогает, если пользователь начал скачивание торрента до подключения к нашему VPN-у (хотя моя практика показывает, что в таких случаях правозащитные ищейки молчат, что еще раз служит подтверждением конспирологической теории, что софт копирайтеров просто отслеживает announce запросы к трекерам), и конечно же данный вариант не спасает от DHT, которому вообще ничего не нужно: ни трекеры, ни днсы. Но работает же, количество писем от правообладателей сократилось до 1-2х за 3 месяца, возвращаем NXDOMAIN для всех доменных имен в блеклисте и живем припеваючи. Кстати, если кто имеет идеи, как побороть bittorrent в сети - скиньте мне хоть пару строк на mail@smirnoff.sumy.ua, чтоб я примерно знал, куда копать. С меня благодарности в приемном для вас эквиваленте.

Возвращаемся к теме реализации. Прежде всего, отвечаю на вопрос: зачем мне нужно много неймсерверов, а не один центральный без всех этих заморочек с MySQL и т.д. В качестве master мы будем использовать Google Public DNS, на нем был замечен load balancing для некоторых крупных сайтов, на запрос по тому же ютубу с разных географических точек отдавались разные A-записи. К тому же, проводить NS query с гонконгского сервера через туннель на центральный сервер где-то в Люксембурге или США - очень медленно, поэтому наши DNS-ы будут отвечать по тому же адресу, что и шлюз. Языком реализации стал Perl. Почему? Да просто в нем есть все необходимые нам компоненты. Использовать мы будем Net::DNS для резолвера и Net::DNS::Nameserver для сервера. Разумеется, без кеширования никуда. В качестве решения я советую смотреть в сторону Cache::Bounded, которое является по сути оберткой вокруг Cache::MemoryCache с оптимизацией и обнулением кеша по достижению определенного размера, что спасет нас от переполнения памяти. В результате получилось что-то вроде такого кода. Сразу замечу, что 3 цикла for при чтении ответа из кеша - необъяснимая мною мистика. Казалось бы с помощью тех же элементов исходного массива путем перебора формируем новый, а вот простое присваивание - не работает. И хоть ты тресни. По скорости вполне нормально, первый запрос конечно отнимает определенное время, но в дальнейшем из кеша в памяти все читается очень быстро. В качестве бонуса, держите список из 1800 самых крупных торрент трекеров для блокировки.

Как по мне, сервисы vpn туннелей должны служить исключительно 2м целям: скачивание торрентов в странах, где за это находят и бьют по рукам; и просмотр католическими священниками детской порнографии там, где это тоже не особо приветствуется. Первых случаев становится все больше, а последних - все меньше. Мы же нашим TOS-ом запрещаем основные приоритетные направления. А все остальные истории с  защитой вашей информации, все это сказки для детей. Мы то с вами знаем, что от паяльника еще ни один фаерволл или канал с шифрованием не спасал.