вторник, 11 октября 2011 г.

Clojure Killer App (updated)

Для каждого языка программирования обязательно нужна хотя бы одна технология, которая сделает его (язык) популярным. Для Java -- это J2EE, для Ruby -- Ruby on Rails, для Python -- Django. Все это так или иначе связано с вебом. Поэтому, когда появился Clojure, многие ждали, что появится и качественный фреймворк для разработки сайтов на Clojure. Однако, сегодняшний уровень развития web-технологий для Clojure пока что не производит впечатления. Может быть потому, что Clojure предназначен быть номером один в несколько других областях?
19 сентября Nathanmarz открыл исходники проекта Storm, проекта, используемого в Twitter для анализа потоков твитов. Storm -- детище Backtype, недавно купленной Twitter-ом. У Backtype есть еще несколько открытых проектов, среди которых -- Cacalog (Clojure-обертка над Hadoop) и ElephantDB.
И Hadoop, и Storm -- технологии, позволяющие легко параллелить вычисления на кластере. Главное отличие между ними в том, что Hadoop выполняет конечные map/reduce задания, в то время, как Storm способен обрабатывать бесконечные потоки данных. Hadoop -- это технология пакетной обработки данных, а Storm -- обработки в real-time.
Storm почти целиком написан на Clojure. 98% функционала Storm -- это Clojure. Правда, если посчитать процент кода на Clojure и процент кода на других языках -- то здесь Clojure займет около 50%. Остальной код -- это привязки к прочим языка программирования, таким как Java и Python.
У Storm-а несколько своеобразная терминология. Минимальными структурными единицами проекта на Storm являются источники (spout) и фильтры (bolts). Термин bolt сложно перевести на русский язык более-менее правильно, но его суть в том, что bolt преобразовывает поток данных из источника в другой поток данных. Так что воспользуемся значением глагола to bolt -- просеивать через сито. Хотя как знать, может Nathanmarz имел в виду именно фонтан (spout) и молния (bolt) -- это я пока что не успел выяснить.
Источники и фильтры объединяются в топологию, про которую подробнее стоит почитать здесь.
Ниже -- пример очень простого приложения на Storm.
(ns storm-test.core
  (:import [backtype.storm StormSubmitter LocalCluster])
  (:use [backtype.storm clojure config])
  (:gen-class))

(defspout number-spout ["number"]
  [conf context collector]
    (spout
     (nextTuple []
                (Thread/sleep 100)
                (emit-spout! collector [17]))
     (ack [id])))

(defbolt neg-bolt ["neg"] [tuple collector]
  (let [result (- (.getInteger tuple 0))]
    (emit-bolt! collector [result] :anchor tuple :p 2)
    (ack! collector tuple)))

(defn mk-topology []
  (topology
   {1 (spout-spec number-spout)}
   {2 (bolt-spec {1 :shuffle} neg-bolt)}))

(defn run-local! []
  (let [cluster (LocalCluster.)]
    (.submitTopology cluster "test" {TOPOLOGY-DEBUG true} (mk-topology))
    (Thread/sleep 1000)
    (.shutdown cluster)))
Функция run-local! создает фиктивный кластер на одном компьютере, позволяющий просто запустить программу локально. На этот кластер подается топология, созданная функцией mk-topology.
В функции mk-topology вызывается topology с двумя параметрами: словарем источников и словарем фильтров.
Запись {1 (spout-spec number-spout)} означет, что создается spout с айдишником 1 при помощи функции number-spout.
Аналогично, запись{2 (bolt-spec {1 :shuffle} neg-bolt)} означает, что создается bolt с айдишником 2 при помощи функции neg-bolt, будет получать данные из источника 1.
Функция number-spout создает spout, который 10 раз в секунду способен выдать вектор с одним единственным данным. Предполагается, что данные spout должен получать из внешних источников, например, очередей сообщений, таких как Kestrel. Здесь же для наглядности я просто передаю число 17.
Функция neg-bolt получает данные из number-spout и отдает их же, но со знаком "минус". Параметр tuple -- это объект класса Tupple, умеющего извлекать типизированные данные из внутреннего массива.
Файл проекта project.clj выглядит так:
(defproject storm-test "1.0.0-SNAPSHOT"
  :description "My first Storm project"
  :aot :all
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [org.clojure/clojure-contrib "1.2.0"]]
  :dev-dependencies [[storm "0.5.3"]])
Запускать проект нужно через lein repl следующим образом:

> (use 'storm-test.core)
> (run-local!)

Короче, думаю, час Clojure настал.

Updation: Выяснил у Nathanmarz-а насчет терминов. Вот что он пишет:
 "Spout" is meant to mean "source of streams", like how a water spout
is a source of a stream of water.
"Bolt" is meant to refer to lightning, as in a "storm" is composed of
"lightning bolts".

Таким образом, думаю, стоит переводить термины следующим образом:
spout -- источник
bolt -- молния.

8 комментариев:

  1. народ уже активно пишет spouts для storm. Я видел JSON input/output & AMQP input source

    ОтветитьУдалить
  2. Круто, спасибо за подсказку!
    Я со Storm-ом только начал пока что, как доберусь до чего серьезного -- тоже буду описывать.

    ОтветитьУдалить
  3. На .NET для работы с очередями событий есть Rx Framework и StreamInsight. Чем интересна реализация на Closure?

    ОтветитьУдалить
  4. Стоит добавить, что красоты в записи всего этого я не увидел. С другой стороны, Rx & StreamInsight позволяют сомпоновать обработчики очередей событий, используя LINQ.

    ОтветитьУдалить
  5. А вы, любители C#-ов вообще редко видите красоту за пределами Microsoft. Что до "интересности реализации", ну так Twitter сделан, между прочим, не на Rx, а GoogleFS появился задолго до StreamInsight. Впрочем, о качестве этих продуктов Microsoft тоже еще можно поспорить, ведь именно Google и Twitter умеют масштабироваться на кластера чудовищных размеров.

    ОтветитьУдалить
  6. А можно пару юз кейсов для простых смертных ?

    ОтветитьУдалить
  7. Storm используется там, где необходимо обработать громадное количество данных, но один кортеж за раз. При этом данные поступают постоянно, т.е входной поток бесконечен. В Twitter с помощью Storm анализируются тренды в твитах, например, "горчие" темы и т.д.
    Storm -- это достаточно общий фреймворк. Его можно использовать в том числе и как механизм Distributed RPC.

    ОтветитьУдалить
  8. Вон, недавно, в Мелкософте стали делать коннекторы к Apache Hadoop. Сами они map/reduce до сих пор ниасилили! Prooflink: http://blogs.technet.com/b/port25/archive/2011/10/12/microsoft-hadoop-and-big-data.aspx

    ОтветитьУдалить