Обзор Mac Mini 2011
пост написан и отправлен в печать 2011-10-16 примерно в 18:55
Привет. Я купил Mac Mini 2011. Он охуенен, мы подходим друг-другу и я хотел бы сделать обзор, но не могу. Мало того, что меня уже даже в в карманном твиттере, состоящем на чуть более чем 100 процентов из безвольных неудачников и стервозных самок, заклеймили дрочером, анальным оккупантом и прочими терминами, которые порочат мою честь и деловую репутацию, так еще вы сами можете невозбранно нагуглить тысячу многостраничных ссылок на разных языках, посвященных исключительно одной теме - обзору алюминиевой коробки с некоторым количеством портов ввода-вывода. Давайте просто поговорим о мотивах и отсутствии альтернатив, как мы это с вами феерически провернули с небольшим ревью iPad-а.

Все производители техники - долбоебы. К такому выводу легко прийти, изучив предложение на рынке по вполне распространенному запросу. Мне нужна рабочая станция с приличным быстродействием. Без последних видеокарт с гигабайтами видеопамяти и сотнями каких-то пиксельно-шейдерных конвееров, нет. Я уже взрослый мальчик, если я захочу крови, то не виртуальной в каком-то коллофдьюти, просто пойду ночью погуляю в дорогих шмотках на вокзал или в городской парк. Мне компьютер нужен исключительно для быстрой и комфортной работы с IDE, десятком открытых вкладок, парой документов, запущенной виртуальной машиной. Ну и диагональю экрана не меньше 26". Разные люди советовали купить ноутбук, подключать его каждый раз к монитору, но это же хуйня какая-то, простите. Зачем мне на столе кусок пластмассы или металла диагональю минимум в 13, если даже не 15 дюймов, со своим дисплеем, когда я не собираюсь куда-либо его выносить. На самом деле все лептопы - тупиковая ветвь эволюции компьютерной техники. Тяжелые, габаритные, с маленьким размером экрана и достаточно ограниченным автономным временем работы, с неудобной клавиатурой и сенсорной панелью, достаточно хрупкие для своей мобильности - все это постепенно отойдет в прошлое, а воссоединение десктопных и мобильных операционных систем, ориентированных на сенсорность, в лице iOS и особенно Windows 8 - яркое тому подтверждения. И тем не менее, есть еще вторая группа: гробы, сраные черные башни, пылесборники из ужасной пластмассы и листов жести, наполненные на 80% пустотой, паутиной и висящими проводами. При этом, достаточно просто взглянуть на ассортимент одних только корпусов для Tower-ов чтобы понять, что этот мир мертв уже очень много лет. Вы наверняка заявите, мол, дизайн - дело второстепенное. Ага, стань директором Норникеля и приезжай в аэропорт на шестерке. Нет, ну правда же, на нее детали дешевые и эпоксидная розочка на коробке переключения передач добавляет романтики в скучную жизнь миллионера. Или простите, а зачем люди делают ремонт в квартирах? Построй свой дом, утепли его и живи в кирпичных стенах, ходи по бетонной стяжке на полу. Или сделай такой крутой ремонт с многоуровневыми потолками и прочей безвкусной хуитой, но вместо шкафа для одежды завези несколько крупных картонных ящиков из-под холодильников. Или хер с ней, с мебелью, купи замок в стиле барокко, привези платяной шкаф викторианской эпохи, положи на пол шкуры гепарда, смотайся на выходные на Монмартр и в базилике Sacré Cœur укради витражи себе на окна, но блять вместо красивой люстры, или что там сейчас для освещения вещают на потолок, спусти фазу, ноль и изолентой прикрути лампочку ильича. Нет, ну правда, свою функцию же данное приспособление исполняет. К чему были данные аналогии - я пытался показать, что все вещи, расположенные на своей собственной жилплощади должны радовать глаз, а не вызывать жуткое желание спрятать их в тот самый платяной шкаф, ага. Но дизайн конечно одновременно и вторичен, кому нужна плита из золота высшей пробы с вкраплением самоцветой сибирских россыпей, готовая превратится в лужу благородного металла при попытке сварить картошку своему мужу - скромному чиновнику из какого-то Газпрома, например. В формфакторе "настольный PC" кроме виновника торжества Mac mini, есть еще конечно же неттопы. У них с дизайном и масштабами вроде все хорошо, но тут, как в старой сказке из области квантовой механики, кот вроде и жив, но в то же время и мертв. Ваш покорный слуга дважды поддался своим внутренним комплексам, выраженных в боязни больших прямоугольных предметов, и купил сперва Acer Revo R3600 для HTPC, а потом еще и Zotac ND-01. Я бы сейчас хотел выебнуться перед вами своим юридическим прошлым и сказать на латыни "человеку свойственно ошибаться", но ограничусь только лишь посыпанием головы пеплом. Для HTPC конечно мощностей Intel Atom-а очень даже хватало, торренты он качал резво, а благодаря платформе nVidia ION без малейших проблем справлялся с декодированием ремуксов по 80GB, но вот в повседневной работе меня ждал какой-то полнейший по всем масштабам провал. Марианская впадина, я бы даже сказал. Жопа, глубина которой удивила даже бы Жака Ива Кусто и его видавший виды батискаф. Всегда мечтал использовать эту фразу. И пусть теперь дефективное поколение, рожденное после 90х думает, что же за такая аллегория скрыта в этой фразе. В любом случае, мне было печально делать абсолютно все, я поседел, постарел лет на 20, моя жена перестала спать со мной и стала спать с массажистом, сына перестал называть меня папой. Да чего уж там, даже Google Chrome не мог нормально прокрутить ленту в Google Reader. Мало того, что эта скотина была чуточку быстрее бетонного столба, так еще умудрялась и греть комнату так, что бомжи, спящие на трубах соседней теплоцентрали, приходили ко мне под дверь и жарили на железной двери яичницу. Слава богу их отпугивал шум дурного куллера, который, ведомый своими маниакальными внутренними побуждениями и странностями встроенного биоса, считал нормальным поведением каждые 10 секунд менять скорость от "ураган Катрина" до "легкий ветерок в сосновой чаще". Все это не способствовало нормальной концентрации на рабочем процессе. И снова мои скудные познания латыни не могут выразить всей той глубины чувств, испытываемых при воспоминании о всех тех мучениях, через которые мне пришлось пройти. Это почти как в тебя врезается камаз, только очень медленно.

Как человек, который за свою жизнь неоднократно избавлялся от наркотической зависимости и престартия ставить сердечки на чужие фоточки в контакте, я понимал, что надо было что-то менять. Как раз намечался крупный проект, где минутные простои при загрузке примитивных приложений могли стоить достаточно дорого. Как говорит слоган рекламы центра лечения бесплодности "чего мы только и не пробовали". Кинул клич и повалили письма. Писали из разных уголков нашей большой страны и советовали смотреть в сторону Mac mini. Или Barebone и Mini-ITX. На тот момент оба решения казались мне хуйней: первое - по причине моего нонконформизма и простого человеческого жлобства, второе - по причине того, что я хотел готовый продукт, а не заниматься тем, в чем я абсолютно ничего не понимаю. В прошлый раз это все закончилось Фукусимой. Но, прошли недели, года, в России на выборах победили демократы, в Америке аппарат президента возглавила вьетнаская шлюха-ревашистка, айфоны начали не только говорить, но и мыслить, стали угрозой человечеству и ушли в подполье. Много времени прошло, короче, вот только выбрать ничего так и не удалось. Как я не пытался - все сводилось к гробам с любой системной конфигурацией, но шумным, прожорливым, ужасным, черным прямоугольникам, лишенным сердца. У меня от этого болела душа и я купил Mac Mini. Душа больше не болит, но парадокс отсутствия альтернатив все еще гложет и терзает. Почему все пытаются бездарно скопировать айпад, айфон, макбук эйр, как последнюю тенденцию, но при этом никто не может засунуть нормальный процессор в формат макмини, разобраться с температурой, шумом и производительностью. Это же много проще с инженерной точки зрения, чем сделать телефон, мне кажется. 

Ах, а вы ждали обзор Apple Mac Mini 2011, наверное. Он охуенен, сделан из алюминия и сзади есть некоторое количество портов ввода-вывода. Спасибо.
Почему не нужно начинать с 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 был просто прослойкой для создания логики. Вам же может повезти намного меньше.

И в качестве послесловия: опытный программист и системный архитектор (охуитительно же звучит) в лице меня ищет интересную работу на расстоянии. Интересную не в смысле денежной компенсации или комплексных обедов, интересным и технически сложным должен быть проект и команда. Хотя команда может быть и не сложной, просто опытной и приятной в общении. Рассмотрю любые предложения, отправленные мне на электронную почту, указанную в шапке.
Делаем свой Google: NodeJS + ElasticSearch
пост написан и отправлен в печать 2011-08-07 примерно в 06:45

Прежде чем начать очередное повествование, которое будет абсолютно неинтересно любителям смехуечек (делегация фишкинет встала и молча вышла из зала) - небольшое лирическое отступление. Мне на мыло написал чувак с забавным вопросом, мол где я, везучий сукин сын, нахожу столько проектов под малоизвестный и нераспространенный ноде. Отвечаю: проектов под эту платформу практически нет, языком программирования это назвать нельзя, это скорее server-side runtime environment для javascript, инструментарий для решения определенного круга задач. Меня находят совершенно по другим критериям (мой рост, соотношение размера ступни к диаметру зрачка, могу говорить, как Сталоне, ну вы поняли), я лишь выбираю NodeJS для реализации. А мог бы выбирать ерланговские Mochiweb, Misultin, Cowboy (показывающие даже более быстрые результаты) или пайтоновский Tornadoweb, но мне родной JS как-то ближе к телу, к тому же под него есть отличный репозитарий готовых велосипедов на все случаи жизни под названием NPM. Хотя, возможно, в ближайшем будущем тут появятся посты и про вышеназванные технологии. Интересно? Жми на +1.

Итак, как меня учили в гуманитарном ВУЗе, начнем с постановки проблематики. Ко мне обратился мой знакомый, подрядчик одной крупной фирмы в Британии со следующим вопросом: есть 40 GB документов в HTML формате (при ближайшем рассмотрении, размер сократился до 18 GB, что тоже немало, столько примерно весит html дамп русскоязычной википедии состоянием на 2008 год), информация крайне конфиденциальная (дневники принцесс и королей, ага) и собиралась за столетнюю историю компании, лет 5-6 тому назад фирма перешла на какую-то свою систему хранения документации, но старый дамп остался статичным и требовал индексации для поиска по нему. Интранет не позволял использовать Google, импортировать все в новую систему документооборота никто не хотел, кому нужен информационный мусор при какие-то результаты санитарных анализов воды в Нью-Гэмпшире за 1928 год? Нужно отдать должное сотрудникам этой конторы, они усердно оцифровывали все и толково составляли каталоги вручную, а значит к каждому документу был доступ по какой-то гиперссылке с другой страницы. Готовые решения я даже не искал по определенным техническим причинам, было принято решение делать свой crawler + indexer, что в наше время оказалось вполне тривиальной задачей.

Самого паука, который будет путешествовать по пыльному архиву, написать, как два пальца об асфальт, об этом мы поговорим чуточку позже, остановимся лучше на технологии поиска и хранения индекса. Всякие реляционные базы данных отпали сразу после того, как я выполнил du -sh в директории с каталогом. Хотел было попробовать сделать что-то свое на основе NoSQL, но из всех известных мне движков не позволял мне искать подобным образом (чуть позже я случайно наткнулся на Riak, увы, было уже поздно), только MongoDB мог похвастаться поддержкой регулярок при выборке, а про полноценный же full text и речь не шла, в официальном мануале советовали разбивать текст на ключевые слова, но это ужасное решение. Именно поэтому было принято решение хотя бы здесь не изобретать двухколесные механизмы и использовать готовые удобные решения. Я успел посмотреть на Apache Solr и ElasticSearch, которые построены на Java Lucene и на Sphinx. Последний сразу отпал, так как его преимущества в виде ненужных мне индексации SQL баз данных и своего Query Language для сложных выборок были сведены на нет необходимостью писать отдельную XML pipe для добавления статики. Solr, имеющий великолепный административный интерфейс, большое коммьюнити, возможность индексировать DOC и PDF (ну мы и сами можем использовать Apache Tika в любом другом движке) показался каким-то слишком сложным для быстрого старта и избыточным, плюс ему не хватало внятной документации (доки в wiki это FFFUUUU). Поэтому я и остановился на ElasticSearch, который может похвастаться великолепным REST API на основе JSON, русской морфологией из коробки, различными текстовыми анализаторами и возможностью сочетать их в любой последовательности, либо даже создавать свои собственные, встроенными языками скриптования (js или python) для выборки и фильтрации, многими вариантами storage для нашего индекса, кучей параметров сортировки и ранжирования, подсвечиванием результатов, wildcard запросами и AND, OR ключевыми словами, простым созданием кластера и репликацией между нодами, даже возможность использования поискового движка в качестве key-value хранилища данных при правильном маппинге. И да, все работает без малейшей конфигурации в стиле load`n`run, я только изменил network.host на 127.0.0.1 и после запуска получил работающее хранилище нашего индекса. А еще у еластика есть, если и не идеальная, то хотя бы внятная документация и крутое Java API, которое, впрочем, нам не понадобится.

Теперь можем переходить непосредственно к написанию нашего crawler-а, который будет переходить по ссылкам и отправлять данные в ElasticSearch. Прежде всего, я написал свою обертку вокруг RESTа еластика для облегчения процесса индексации и поиска, она очень простая и реализует только функционал двух функций - index (плюс update) и search. Код обвязки можно посмотреть. Его мы будем использовать, как в нашем поисковом боте, так и в серверном приложении, возвращающем ответ пользователю. Теперь непосредственно о преимуществах NodeJS в этой сфере. Прежде всего, это мультипоточность из коробки, разумеется, на всех современных языках можно добиться аналогичного результата (ну разве что кроме похапе, но оно там абсолютно не нужно); и, второй по значимости плюс, - jQuery. Да, я не опечатался, мы будем использовать селекторы и модификаторы jquery на серверсайде. Все это возможно благодаря великолепной библиотеки JSDOM, которая позволяет нам получить виртуальный DOM из кода HTML разметки и эмулировать все вызовы к нему. Почему jQuery? Ответ прост - банальная лень в ущерб определенной производительности, конечно же мы можем использовать регулярки для выборки всех ссылкок, но $('a').each() выглядит проще и симпатичней. Столкнулся с необычной проблемой, которая описана в том числе и в коде самого краулера: ElasticSearch почему-то наотрез игнорировал точки, двоеточия, слешы и прочие символы, поэтому сделать проверку статуса индексации ссылки через ее url не получилось, для этой цели используется md5 хэш. В целом, получилось достаточно быстрое решение, данный блог, скормленный своей главной страницей нашему боту, был полностью добавлен в кеш еластика за полторы минуты всего-то в 3 потока. Я старался, по мере возможности, комментировать исходный код, надеюсь, что у вас не возникнет проблем с его чтением. В идеале, я советую использовать 10 потоков с queueInterval около 100, если ваши сетевая подсистема и процессор выдержат подобные высокие нагрузки, с такими значениями мне удавалось индексировать около трехсот страниц википедии в минуту на средненьком железе и канале в 30 mbit\s. Паук проверяет вхождение домена индексируемой ссылки в разрешенный список и ищет дозволенный content-type в ответе, реализует своеобразный robots.txt при помощи массива регулярок indexQueryPath. К тому же возможна переиндексация при достижении определенного возраста индекса, у меня, по умолчанию, это 100 дней. Процесс индексации в вашей консоли будет выглядеть как-то так. Работающий скрипт в memory leaks замечен не был, но вот JSDOM и jQuery очень любят процессор, будьте готовы к высоким нагрузкам. Для демонстрации я прошерстил этот бложек и написал небольшое приложение поиска по всем постам, состоящее из такой вот серверной части. Его работу можно посмотреть в лаборатории им. Бена Ганна по ссылке.

В заключение, хочу выразить благодарность разработчикам и сообществу NodeJS и ElasticSearch. При всей моей нелюбви к красноглазому opensource, два этих великолепных программных продукта, создаваемых на чистом энтузиазме, помогли мне сделать работу в срок, обрадовать вычурных британцев и купить новую дозу героина. Очень сложно затягивать жгут на руке и писать сюда, до новых встреч, друзья.

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>.