Wednesday, February 22, 2012

Coding Dojo - экстремальное погружение

   Не так давно мне посчастливилось попарнокодить с Johannes Brodwall. Один из результатов спаринга я уже опубликовывал в предидущем блоге (см. Зачетный пример на TDD)
   К сожалению мне не удалось попасть на его мастер-класс "TDD Coding Dojo", так как именно в этот день я обещал своей жене то, чего она ждала последние 7 лет - пойти с ней на танцевальную вечеринку. Можете себе представить мою досаду, когда мы, одетые, побритые и накрахмаленные пришли вечером в танцевальный клуб и узнали, что вечеринку перенесли на другой день?!. Еще раз сделаю ссылку на сайт танцевельной тусовки дабы чаще смотреть обновления об изменении планов.
   В общем я задался целью организовать это мероприятие у нас на фирме. Слава Богу, контора большая и желающих должно набраться большое количество. Тем более, что Саша развил в киевском офисе клевую идею с техтолками, что уже создало своеобразную тусовку.
   Итак, я вооружился инструкциями "Как стартануть Coding Dojo" от Johannes'a и решил попробовать запустить это у себя на компе

Как это работает


   Coding Doje Extreme Setup состоит из 2-х частей - клиентская (я ее буду называть Extreme Setup хост, или просто хост) и серверная. Хост - это программа, которая 1 раз в 10 секунд посылает http запросы всем зарегестрированным серверам участников и сравнивает их ответы с правильным вариантом. Если ответ правильный - игрок получает очки, если ответ неправильный - очки отнимаются. Включив принцип КО можно догадаться, что сервер - это программа, запущенная на компьютере участника. В случае с Java - это обычный сервлет

Как установить хост


   Для одной игры хост нужен только один, так что если вы собираетесь только играть можете смело перепрыгивать в секцию Установка сервера.
   Клонируем git://github.com/jhannes/extreme_startup.git. Это форкнутая отсюда https://github.com/rchatley/extreme_startup версия. Выбрал ее потому, что она отработана на киевском dojo на XPDays. Я склонировал в папку C:\workspace\projects\dojo-startup\extreme_startup
   Дальше делаем все согласно README.md из проекта, а именно:
Устанавливаем Ruby 1.9.3 (качаем отсюда)


   Скачиваем Ruby DevKit и распаковываем в (я установил в c:\workspace\bin\RubyDevKit)


   Далее устанавливаем DevKit:
- Открываем командную строку, переходим в c:\workspace\bin\RubyDevKit и запускаем ruby dk.rb init.

- Сгенерировался config.yml файл. Откроем и посмотрим. Должны увидеть путь к установленному Ruby.

Если нет – надо добавить

- Запускаем ruby dk.rb install



   Переходим в папку со склонированным репозиторием (C:\workspace\projects\dojo-startup\extreme_startup) и запускаем команду gem install bundler.

   Затем запускаем команду bundle install и ждем, пока скачаются и установятся необходимые библиотеки. После появления обнадеживающей надписи

   запустим extreme startup сервер в режиме разогрева (warmup mode): ruby warmup_web_server.rb

   Проверяем страничку с результатами http://localhost:3000 (пока пустая). Проверяем, что хост доступен с другого компьютера.

   Чтобы присоединиться к игре необходимо ввести имя и URL веб сервера игрока, запущенного на его машине. Для каждого языка программирования существует свой простой способ стартануть сервер. Я привел инструкции как это сделать на Java. Для других языков смотрите список проектов в каталоге extreme_startup_servers
   Ждем, когда все игроки настроят свои средства разработки и зарегистрируются. Как только все прокричали «я готов!» останавливаем сервер и запускаем реальную игру командой ruby web_server.rb

Установка сервера на Java.


   Прежде всего нужно убедиться, что установлен Maven (если нет, то утанавливаем отсюда) и он доступен по путям: mvn –version что-то выдает
   Скачиваем зип архив отсюда. Это содержимое репозитория со старт-поинтами https://github.com/bodil/extreme_startup_servers. Я склонировал в папку C:\workspace\projects\dojo-startup\extreme_startup_servers
   Запускаем любимую java IDE (в моем случае это IntelliJ IDEA) и создаем новый проект из мавеновского pom.xml. Проект берем этот: /java/servlet_for_dummies (C:\workspace\projects\dojo-startup\extreme_startup_servers\java\servlet_for_dummies)

   После открытия проекта смотрим что внутри


  • ExtremeStartupServer – класс с main методом для запуска Jetty
  • ExtremeStartup – сервлет, который мы будем программировать
  • ExtremeStartupTest – тест, который нам поможет этот сервлет запрограммировать

       Запустим ExtremeStartupTest – если все тесты зеленые, то запускаем сервер (ExtremeStartupServer)
       Открываем браузер и вводим адрес http://localhost:1337/?q=what+is+the+sum+of+8+and+22


       Все, теперь можем регистрировать наш сервер. Переходим на страничку хоста (http://localhost:3000 или в случае другого компа – IP адрес этой машины), нажимаем I want to play и вводим имя (латинскими буквами!) и URL вашего сервера.

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


       Можем посмотреть результат работы нашей программы. Поскольку мы еще ничего не запрограммировали, то ожидаем примерно такую картинку:


       Выведем на консоль то, что спрашивает хост – делаем изменения в классе ExtremeStartup
       Оказывается хост хочет узнать как меня зовут.


       Не вопрос, поменяем реализацию метода answer
       и смотрим на свой результат. Ура! У меня есть первый правильный ответ


       Итак, в консоли мы видим приходящие запросы и программируем сервлет так, чтобы он отдавал правильные результаты. За каждый правильный результат начисляются баллы, за каждый неправильный или пропущенный ответ вычитаются. Думаю очень полезно использовать тест, чтобы не поломать предыдущую функциональность и иметь возможность быстро сэмулировать запрос.
       Все, можно кричать "Я готов!"
  • Friday, February 10, 2012

    Netty - очередное чудо асихнронной событийности в сети

    Раз уж я так увлекся исследованиями асинхронными событийно-ориентированными сетевыми платформами на Java, то не могу не упомянуть о Netty. Как заявлено на сайте Netty - это асинхронный событийный фреймворк для сетевых приложений
    Не будем утруждать себя чтением доки (хотя иногда это очень стоит делать), а сразу реализуем такой же примерчик, какой мы делали для демонстрации node.js
    Как обычно, создаем мавеновский проект. Вот pom.xml:

    Обрати внимание на то, что здесь добавлен Мавен репозиторий JBoss
    Создаем класс самого сервера:
    Чуть подробнее о классах, которые он использует:

    ChannelFactory это фабрика для создания и управления Channel'ами и их ресурсами. Channel в Netty - это аналог Channel'ов из пакета java.nio.channels с возможностью асинхронного вызова чтения/записи.
    NioServerSocketChannelFactory - это реализация ChannelFactory. Конструктор принимает 2 Executor'a : bossExecutor и workerExecutor. Boss потоки обслуживают открытые серверные Channel's (ServerSocketChannel). Worker потоки осуществляют неблокирующее чтение/запись для одного или нескольких ченелов. Т.е. если, например на сервере открыто 2 порта 80 и 443, то их обслуживать будут 2 Boss потока. Как только сервер примет клиентское соединение, то boss передаст принятый Channel одному из worker потоков.
    ServerBootstrap - это вспомогательный класс для инициализации сервера. Можно и без него, но прийдется много кода писать
    Дальше мы конфигурируем ChannelPipelineFactory. Как только сервер приймет новое соединение, то будет создан новый ChannelPipeline этой фабрикой.
    Вот наша реализация ChannelPipeLineFactory

    Немного о том, что такое ChannelPipeline. Как только создан новый Channel, то с ним ассоциируется новый экземпляр pipeline. Pipeline содержит набор обработчиков событий, которые вызываются при получении сообщения или записи. При этом принята метафора, что все сообщения на чтение обрабатываются восходящими обработчиками (Upstream Handler), а на запись - нисходящими (Donwstream Handlers).
    Например, если клиент подключился к серверу и послал GET запрос, то вызовутся Upstream обработчики, то есть те, которые реализуют интерфейс ChannelUpstreamHandler. Когда мы сформировали ответ и отдаем его клиенту вызовутся обработчики, которые реализуют интерфейс ChannelDownstreamHandler. Причем Upstream вызовутся в порядке, котором их определили при добавлении, а Downstream - в обратном направлении.
    В нашем случае при получении сообщения вызовется сначала HttpRequestDecoder, а затем наш HelloHander
    Обратите внимание на то, что здесь идет 2 раза запись в Channel. При chunked ответе содержимое обьекта response автоматически станет пустым. Поэтому Hello пришлось записывать отдельным блоком и дальше создавать наши асинхронные таски с помощью AsyncExecutor
    Поскольку вся запись/чтение в ченел асинхронные, то для того, чтобы сделать действие действительно после того, как запись произойдет нужно зарегистрировать листенера, который вызовется после физической записи блока в поток ввода-вывода
    Еще один интересный момент - если у нас чанкед ответ, то HTTP спецификация просит последний блок (chunk) делать пустым
    Запустим приложение и дадим команду из командкой строки curl -i http://localhost:8081. В результате выведутся http headers, за ними Hello и World 0, а затем с интервалом в 2 секунды - World N.

    Вывод.Судя по архитектуре фреймворк должен быть устойчив к большому наплыву пользователей (масштабируемость). Очень легковесный, jar файл занимает 800К. Сложная в разработке и понимании (одна метафора с событиями чего стоит). Отлично подошла бы для специфических задач, например для веб серверов, которые передают/принимают большие потоки данных, или для систем, где большое количество пользователей делают однотипные операции (AJAX чаты, месенджеры и т.п.). Медиа стриминг, говорят, тоже отлично решается с помощью этого фреймворка.

    Servlet 3.0 - лучше позже чем никогда

    Наконец-то после 6 лет с момента реализации технологиии Comet в Jetty и 2х лет со дня выхода финальной версии спецификации Servlet 3.0 вышел релиз Jetty 8, который эту спецификацию, собственно, и реализует. Кроме аннотаций и прочих рюшечек самое интересно новшество - это, конечно, возможность асинхронной обработки запроса, что сильно упрощает процесс Server Push'a. Посмотрим на примере как реализована эта возможность.
    Создаем проект. Для этого нам понадобится средство сборки Maven. Создаем мавеновский проект и прописываем зависимости. Вот полный код моего pom.xml:
    Реализуем такой же примерчик, как тот, что я делал во время демонстрации node.js
    Вот таким будет наш сервлет:

    В аннотации сервлета есть важное свойство asyncSupported. Это значит, что наш сервлет может переводить запрос в асинхронное состояние. Перевод делается методом startAsync()
    Мой класс AsyncExecutor, над реализацией и чистотой кода которого я особенно не думал:
    Теперь запустим наше приложеньице. Удобно делать это с помощью jetty-maven-plugin. Я делаю это из IntelliJ IDEA:

    Теперь посмотрим как это работает и заодно глянем на header'ы. Запускаем curl -i http://localhost:8080/hello. Сразу выводится Hello и World 0, затем World N - каждые 2 секунды

    Ура! Работает!

    Node.js - все гениальное просто

    Недавно посмотрел ролик Райана Дала (Ryan Dahl), идейного вдохновителя и создателя платформы для быстрых и масштабируемых сетевых приложений node.js. Очень понравилась простота и легковесность этого решения. Node.js - модная и активно развивающаяся платформа, интересно, что ее уже активно используют в eBay. Собственно, я и начал это исследование, вдохновленный идеей ql.io от eBay (тут анонс)

    1. Инсталлируем
    Скачиваем отсюда и ставим как обычно. Я еще добавил в пользовательские пути

    Пробуем запустить из консоли. Открылась консоль, в которой можно побаловаться и позапускать Javascript функции и выражения.

    Node.jsпостроен на javascript движке V8 от google. Этот движон используется в chrome. Естественно, что в консоли node нету обьектов браузера, но можно посмотреть process()

    2. Hello World!
    Напишем такую вот програмку:

    И запустим. После чего сначала появится Hello, затем через 2 секунды world


    После выполнения - вернулись в командную строку. Теперь попробуем сделать так:


    Приложение не завершается, т.к. есть что делать (колбек, который надо вызывать каждые 2 сек)


    3. Пишем веб-сервер
    Ок, Javascript в консоли - это, конечно, очень круто, но давай напишем серверное приложение


    И запустим его. Интересно посмотреть на http headers. Смотрим с помощью curl это curl –i http://localhost:8000


    4. Делаем Server push
    Hello World - это элементарно. Концепция асинхронной событийной модели node.js позволяет так же элементарно реализовывать server push, как и в примере с выводом на консоль.

    Этот примерчик немножно посложнее. После Hello каждые 2 секунды будем выводить World N, где N - это значение каунтера. Будем выводить, пока не надоест, а надоест нам через 5 выводов



    Вывод. В этом примере, конечно, не рассматриваются преимущества асинхронной событийной модели при разработке, но такой подход все чаще используется. Это связано с возросшей нагрузкой на серверные ресурсы, которая особенно стала заметна после появления Web 2.0 и постоянно растущем количестве nice-and-sexy online приложений где пользователю не надо жать Refresh чтобы получить новую порцию данных.
    Подробнее о преимуществах и как это устроено можно посмотреть здесь или здесь или в моей презентации с Ciclum Java Saturday, которую расшарю позже