Try using immutant.messaging

ちょっと Immutant の Web 以外のものを試したくて、 Messaging を試してみていた。

REPL の中だけで試すなら project.clj の依存性のところに [org.immutant/immutant "2.0.2"] を含めておいて次のコードを試したらいい(それか lein try などを使って REPL を起動すれば良い)。

(require '[immutant.messaging :as msg])

(def q (msg/queue "q"))

(msg/listen q println)

(msg/publish q "try queue")

listen したものを閉じてないとかお行儀の悪さはここでは大目に見てほしい。

Immutant の Messaging 部分は HornetQ というメッセージングシステムが使われていて、これ自体は Java の仕様にあるところの JMS に沿っているらしい。この辺は僕が疎いところなので結構大変なんだけど、まぁ調べながら使ってみたという話。

JMS には 2 種類のメッセージング方法が用意されていて、 Point-to-Point と Pub/Sub 形式があるらしい。上の例は Point-to-Point だったけど、今回の僕が使いたい目的も Point-to-Point の方が良さそうなので今回はこっちを使ってみようと思う。

今回作りたい構成

  • Stand alone な HornetQ サーバー
  • メッセージを受信するクライアント
  • メッセージを送信するクライアント

3 つ目のものは面倒なので REPL で実現してしまう。これが Web アプリケーションに変わったところで対して違いはないだろうことは想像に難くないので。 なので 1,2 のサーバーと受信クライアントを簡単に用意してみようと思う。

Stand alone HornetQ サーバーの配置と起動

今回は Downloads ページから 2.4.0.Final をダウンロードしてくる。 zip でも tar.gz でも、どちらでもいいけど解凍すると hornetq-2.4.0.Final というディレクトリが現れると思う。 hornetq-2.4.0.Final/bin/ の中に潜り ./run.sh を実行すると Stand alone HornetQ サーバーが起動できる。だけどこの段階ではまだ自分が使いたい queue の名前を登録出来ていないので一度 ./stop.sh を実行するなどして止める。

hornetq-2.4.0.Final/config/stand-alone/non-clustered/hornetq-jms.xml を開いて、 <configuration> の直下の階層に次のコードを足す(他にも queue というノードがあるのでその下あたりに適当に追加しておくと良さそう)。

<queue name="hello">
   <entry name="/queue/hello"/>
</queue>

これで hello という queue が出来たので、クライアントから接続することが出来る。ということで先ほどと同様に hornetq-2.4.0.Final/bin/ の中に潜り ./run.sh を実行すれば hello という queue に接続できます。一旦、サーバーは落としておいて次に受信クライアントを作成します。

最終的にやったのは以下のコマンドで同様に実行できます。

$ curl -O http://downloads.jboss.org/hornetq/hornetq-2.4.0.Final-bin.tar.gz
$ tar xvzf hornetq-2.4.0.Final-bin.tar.gz
$ emacs ./hornetq-2.4.0.Final/config/stand-alone/non-clustered/hornetq-jms.xml
$ # modify xml file
$ cd hornetq-2.4.0.Final/bin/
$ ./run.sh

受信クライアントの作成

本当はこっちも REPL でいいんだけど、それだと送信も受信も REPL になっちゃってあまり面白くないので軽く作る。

lein new hornetq-child とでもしましょう。 project.clj を次のように修正します。

(defproject hornetq-child "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.immutant/immutant "2.0.2"]]
  :main hornetq-child.core
  :aot :all)

依存性に [org.immutant/immutant "2.0.2"] を追加、コンパイルして実行可能 jar にしたいので :main:aot を追加しました。

次に core.clj を修正します。

(ns hornetq-child.core
  (:require [immutant.messaging :as msg])
  (:gen-class))

(defn -main [& args]
  (with-open [context (msg/context :host "localhost" :port 5445)
              listener   (msg/listen (msg/queue "hello" :context context)
                                     (partial println "[info] ")
                                     :context context)]
    (while true
      (Thread/sleep (* 1000 1000 1000)))))

都合 main 関数の中で無限ループ使ってるけど、まぁ今回の本質はそこではないのでいいでしょう。

軽く解説すると msg/context はリモートのサーバーを指したいときに使う。これを使わないで msg/queue を呼び出すとローカル(というか、同一 JVM 上?っていうほうがいいのかな)に HornetQ サーバーを起動してそこに queue を作成してしまうので注意。 msg/listen で第一引数として msg/queue で作成したデスティネーションを受け取って、第二引数には引数をひとつ取る関数をコールバックとして渡せて、あとはオプションでここでもコンテキストを渡している。

ここまでできたら後は lein uberjar とでもしてしまって、 jar を作ってしまう。 java -jar target/hornetq-child-0.1.0-SNAPSHOT-standalone.jar で起動可能です。

最後に送信テスト

HornetQ サーバーの準備とメッセージ受信クライアントは作成できたので、ふたつとも実行しておきましょう。順番は HornetQ サーバーからですね。

送信側は REPL でいいので lein try org.immutant/immutant "2.0.2" とでもして REPL を起動して、以下の式を評価しましょう。

(require '[immutant.messaging :as msg])

(with-open [c (msg/context :host "localhost" :port 5445)]
  (msg/publish
   (msg/queue "hello" :context c)
   "Hello, world"))

こうすると下の画像のように送信と受信が行われたのが確認出来ると思います。画像中では REPL を Emacs の中で起動していますが、まぁ大きな問題じゃないです(左半分が送信側、右上半分が受信クライアント、右下半分が HornetQ サーバー)。

../../../_images/try-hornetq.png

一応書いておくと msg/publish はデスティネーションと送信したいオブジェクトを受け取りますが、 Clojure らしく edn のフォーマットでオブジェクトを送信できたりします。そのへんはオプションの :encoding で指定できます。

最後に

若干必要に迫られてメッセージングというのを調べていた。今までメッセージングといえば RESTful な API 作って叩いてみたいな感じで生きてきたので、この機会に悔い改めたい。

しかしながら、こういうニッチな(?)サービス/ミドルウェアの情報というのは各社企業秘密というかあまり表立って出てこない情報なので、難しいなと思った。ただ、普段やってるインターネットな感じの非同期処理とはまた違った知見を得ることができたので大変良かった。

Note

サイボウズスタートアップスでは Clojure で Web アプリケーション開発したいエンジニアを募集しています。