Apache Storm DRPCのHTTP APIを使う

Apache Stormのdefaults.yamldrpc.http* の項目があることから分かるように、Storm 0.10.0のDRPCサーバはDRPCクライアントから利用されるほかに、HTTP APIを持っている(0.9.6にはない)。つまり、HTTPクライアント(PerlのHTTPクライアントライブラリでもcurlでもWebブラウザでも)を用いて、DRPCサーバに処理リクエストを投げ、トポロジの処理結果をレスポンスとして受け取ることができる。

何が嬉しいか

HTTPリクエストに対する処理がスケールアウトしやすい

StormのDRPCは、Distributed Remote Procedure Callの略だが、Stormのトポロジの入力を外部から受け付け、トポロジを通して得られた処理結果を外部に返すためのものである。Stormのトポロジは、入力データを流す始点となるSpoutと、データを処理するBoltに分けられるが、DRPCアプリケーションの場合、Spoutは外部からの入力データを受け付け次のBoltに流し、Boltはデータに処理を行いその結果を次のBoltに渡し、最後のBoltが最終的な処理結果のデータを外部に返す。

Stormのトポロジを構成するSpout、Boltはスケールアウトできるように設計されているので、処理が重い部分・並列分散処理させたい部分の並列数を増やす指定をすることで、実行するスレッド(あるいはプロセス、マシン)を増やしたり、逆に処理を分散させたくない部分の並列数を1にしたりもできる。また、クラスタを構成するマシンの台数を増やすことで、アプリケーション内の並列分散処理の性能を高めることができる。マシンを増やした際の作業は、Stormの仕組みのおかげで、非常に簡単で、アプリケーションに変更は必要ない。

DRPCのHTTP APIを用いることで、このStormの並列分散処理の仕組みをそのまま、HTTPリクエストに対する処理に適用することができる。

このメリットのイメージ図を551の肉まん風に書いてみた。


マイクロサービスを作りやすい

1つのStormクラスタに、複数のDRPCアプリケーションをデプロイすることができる。Netflixなどがよく言っている、マイクロサービスのアーキテクチャ実装のために、この仕組みは適している。

1つのDRPCアプリケーションにリクエストに対する重厚長大な処理をさせるよりも、複数のDRPCアプリケーションを1つのクラスタの中に並べ、それぞれのDRPCアプリケーションに小さな処理を担当させた方が、より変化に対応しやすい環境となる。たとえば、スケールアウトしやすかったり、処理のある一部分の変更が容易になる。

DRPCのHTTP API仕様

DRPCサーバのHTTP APIstorm-core/src/clj/org/apache/storm/daemon/drpc.cljを見ると、

  • GET: http://DRPCサーバ名:DPRCのHTTPポート番号/drpc/関数名[/引数]
  • POST: http://DRPCサーバ名:DPRCのHTTPポート番号/drpc/関数名[/]、リクエストボディに引数

となっていることがわかる。引数を省略した場合、空文字が関数に渡される。GETとPOSTの両APIに実行時の違いは無さそうなので、クライアントの都合でどちらを使うかを選択すれば良いようだ。

実際に使ってみた。

Stormクラスタのセットアップ

最低限の設定として、ZookeeperとNimbus、DRPCサーバのアドレスを指定する(すべてlocalhost)。それを使ってコンポーネントを起動する。

$ curl -O http://ftp.meisei-u.ac.jp/mirror/apache/dist/storm/apache-storm-0.10.0/apache-storm-0.10.0.tar.gz
$ tar zxvf apache-storm-0.10.0.tar.gz; cd apache-storm-0.10.0/
$ cp conf/storm.yaml conf/storm.yaml.default
$ vim conf/storm.yaml
$ diff conf/storm.yaml.default conf/storm.yaml
18,23c18,22
< # storm.zookeeper.servers:
< #     - "server1"
< #     - "server2"
< #
< # nimbus.host: "nimbus"
< #
---
> storm.zookeeper.servers:
>     - "localhost"
>
> nimbus.host: "localhost"
>
37,39c36,37
< # drpc.servers:
< #     - "server1"
< #     - "server2"
---
> drpc.servers:
>     - "localhost"

$ ./bin/storm dev-zookeeper &
$ ./bin/storm nimbus &
$ ./bin/storm supervisor &
$ ./bin/storm drpc &

Storm DRPCアプリケーションのデプロイ

DRPCストリームを含むStormトポロジを、Stormクラスタにデプロイする。今回はapache-storm-0.10.0.tar.gzに同梱されている、storm-starterから2つ拝借する。

$ ./bin/storm jar \
  examples/storm-starter/storm-starter-topologies-0.10.0.jar \
  storm.starter.trident.TridentWordCount TridentWordCount
$ ./bin/storm jar \
  examples/storm-starter/storm-starter-topologies-0.10.0.jar \
  storm.starter.BasicDRPCTopology BasicDRPCTopology

DRPCのHTTP APIにアクセス

TridentWordCountは時間が経つにつれストリームに流れた文の量が増え、単語カウントが大きくなっていく。適当なタイミングでアクセスしてみた。対象関数はwordsで、引数として2つ「the」と「eat」をそれぞれGETとPOSTで渡してみた。

$ curl http://localhost:3774/drpc/words/the
[[2679]]

$ curl -X POST -d eat http://localhost:3774/drpc/words/
[[536]]

DRPCクライアントを用いた時と同様の返り値であるようだ。

次に、BasicDRPCTopologyについて。対象関数はexclamationで、引数として「eat」と「Hello」をそれぞれGETとPOSTで渡してみた。

$ curl http://localhost:3774/drpc/exclamation/eat
eat!

$ curl -X POST -d Hello http://localhost:3774/drpc/exclamation
Hello!

こちらも想定通り。

ちなみに、空白を含む文字列などを引数に渡す際にはURLエンコードして渡してやればStormの方でデコードしてくれて、結果もデコードされた文字列で返される。

$ curl http://localhost:3774/drpc/exclamation/The%20cow%20jumped%20over%20the%20moon
The cow jumped over the moon!

DRPCはStormの機能の中でも割とマイナな方だと思っていたが、かなり実用的な進化をしていて嬉しい。drpc.https.* などの設定項目があることから、セキュアな環境もサポートしているようだ。

aitendoのUSBASPが替わってた

AVR-USBASP-Bが在庫なしで、AVR-USBASP-Cが大量にあった。

Bの方はaitendoロゴがありマイコンがATMEGA8Aだったが、CはV2.0 Lcsoft Studioと書いてあってATMEGA8Lを使っている。

ベースはどちらもUSBasp - USB programmer for Atmel AVR controllers - fischl.deのようだが、CはUSBasp AVR Programmer, Program Atmel AVR microcontrollersなどで販売されている派生版のようだ。違いを説明しているブログがここなどにあった。

本家のファームウェアのアップデートは止まっているし、わざわざ新しい既成品を揃える必要は無いかなと思って、買わなかったけどメモ。

myThingsのIDCFチャンネルとESP-WROOM-02とのMQTT双方向連携

myThings外部サービスチャンネル(トリガー) → IDCFチャンネル(アクション) → ESP-WROOM-02

myThingsアプリ上で、Yahoo!天気をトリガーにしたものと、ぐるなびをトリガーにしたものの2つの組み合わせを作成した。それぞれでアクションに渡すメッセージはそれっぽい感じにしている。

ESP-WROOM-02には以下のArduinoコードをビルドしたものをアップロードし、Arduino IDEのシリアルモニタを表示した状態で、Wi-Fi・Meshbluに接続しておく。

myThingsアプリから、2つの組み合わせを手動実行する(あるいは待つ)と、Arduinoシリアルモニタに以下のようなメッセージが出力された。

.............
WiFi connected
IP address: 192.168.10.20
succeeded to connect to mqtt broker
{"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":"東京(東京)の2015-10-17の天気は曇り、最高気温20、最低気温16、降水確率40、風向き北の風、波の高さ0.5メートル","fromUuid":"MYTHINGS_UUID"}}
	東京(東京)の2015-10-17の天気は曇り、最高気温20、最低気温16、降水確率40、風向き北の風、波の高さ0.5メートル
{"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":"肉 × イタリアン Hana 六本木店 〒106-0032 東京都港区六本木7-14-10 誠志堂ビル6F","fromUuid":"MYTHINGS_UUID"}}
	肉 × イタリアン Hana 六本木店 〒106-0032 東京都港区六本木7-14-10 誠志堂ビル6F
{"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":"個室&イタリアン CERTO! 六本木店 〒106-0032 東京都港区六本木4-11-5 アネックスビル1F","fromUuid":"MYTHINGS_UUID"}}
	個室&イタリアン CERTO! 六本木店 〒106-0032 東京都港区六本木4-11-5 アネックスビル1F
{"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":"六本木 PALETTE 〜Italian Tapas〜 〒106-0032 東京都港区六本木7-14-10 誠志堂ビル6F","fromUuid":"MYTHINGS_UUID"}}
	六本木 PALETTE 〜Italian Tapas〜 〒106-0032 東京都港区六本木7-14-10 誠志堂ビル6F
{"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":"豪快 イタリアン食堂 DESERT 〒106-0032 東京都港区六本木7-15-25 六本木7thビル2F","fromUuid":"MYTHINGS_UUID"}}
	豪快 イタリアン食堂 DESERT 〒106-0032 東京都港区六本木7-15-25 六本木7thビル2F
{"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":"六本木バル PIZZANIA 〒106-0032 東京都港区六本木2-3-7 セントラルクリブIII1F","fromUuid":"MYTHINGS_UUID"}}
	六本木バル PIZZANIA 〒106-0032 東京都港区六本木2-3-7 セントラルクリブIII1F

これで、ESP-WROOM-02がmyThingsのトリガーチャンネルからメッセージを受け取れていることが確認できた。

ESP-WROOM-02 → IDCFチャンネル(トリガー) → 外部サービスチャンネル(アクション)

myThingsアプリ上で、IDCFチャンネルをトリガーにしてYahoo!メールをアクションにした組み合わせを作成した。

ESP-WROOM-02には以下のArduinoコードをビルドしたものをアップロードし、Arduino IDEのシリアルモニタを表示した状態で、Wi-Fi・Meshbluに接続しておく。ここでは、DHT11の温湿度センサを接続し、その情報を流している。

しばらくするとArduinoシリアルモニタに以下のようなメッセージが出力された。

.............
WiFi connected
IP address: 192.168.10.20
succeeded to connect to mqtt broker
Humidity: 39.00 %	Temperature: 24.00 *C
{"devices":["MESHBLU_ACTION_UUID"],"payload":{"humidity":39.00000,"temperature":24.00000}}
Humidity: 39.00 %	Temperature: 24.00 *C
{"devices":["MESHBLU_ACTION_UUID"],"payload":{"humidity":39.00000,"temperature":24.00000}}

Meshbluに以下の様なPythonサブスクライバを登録しておくと、メッセージが出力された。


connected with result code0
received message: {"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":{"humidity":39,"temperature":24},"fromUuid":"MESHBLU_TRIGGER_UUID"}} from MESHBLU_ACTION_UUID
received message: {"topic":"message","data":{"devices":["MESHBLU_ACTION_UUID"],"payload":{"humidity":39,"temperature":24},"fromUuid":"MESHBLU_TRIGGER_UUID"}} from MESHBLU_ACTION_UUID

同時に、myThingsアプリのアクションで指定したメールアドレス宛に以下のようなメールが届いた。

これで、ESP-WROOM-02からのメッセージがトリガーとなり、アクションが発火していることが確認できた。

残念ながら、IDCFチャンネルのトリガーメッセージの内容を元にアクションのメッセージを変更する方法がわからなかった。

langtool.elの情報をポップアップ表示できるようにした

GitHub - mhayashi1120/Emacs-langtool: LanguageTool for EmacsLanguageToolデスクトップ版をEmacsから使うためのElispで、ispell (aspell) と共に、英文を書く際にお世話になっている。

標準の使い方だとlangtool-check-bufferでチェックプログラムを走らせ、問題があるとしてハイライトされた部分文字列にカーソルを置いてlangtool-show-message-at-pointでその指摘内容を確認するのだが、この指摘内容の表示がlangtool-error-buffer-nameで指定したバッファ名でウィンドウが開くので、結構見づらい。

そこで、これを GitHub - auto-complete/popup-el: Visual Popup Interface Library for Emacs で表示するようにした。

これで見易くなった。langtool-correct-bufferの修正候補も出せるようになるともっと嬉しいが、そこまでする気力が無かった。

Emacs Lispって、Haskellでいうtakeが無いの?

FeatureFuを使ってみた

FeatureFuはLinkedInが作り公開しているFeature Engineeringのためのライブラリ。

使い方から機能を述べると、数式をS式の文字列として書き、変数に値をバインドしその文字列を評価すると、数式の結果が返ってくる。

検証のためのコード

$ cd ~/
$ git clone https://github.com/linkedin/FeatureFu.git
$ cd FeatureFu/
$ gradle clean jar
$ cd ~/
$ git clone https://github.com/laclefyoshi/test_featurefu.git
$ cd test_featurefu/
$ mvn clean test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

肝となるコードをgistに写した。ここに、現在のFeatureFuで使える演算子 (Operator) が全て列挙されている。

\sum\prodforreduceで書いて、その内側の数式をFeatureFuで書いておけば、使い回しもし易くなる、という考えだろう。論文に書かれた数式をコードに落とすのには慣れが必要だが、こういうライブラリがあれば、考えなければいけないことが1つ減ってありがたい。

OCNモバイルONE開通までの流れ

月末にOCNモバイルONE(音声対応)に申し込んで、開通までの流れを記録した。MNPはしていない。

月末は混むといわれているが、7営業日(+週末2日)で全て完了したので、まあ順調だったな方ではなかろうか。

第1週 月曜

goo SimSellerでgooのスマホ+OCNモバイルONE(音声対応)を注文

第1週 火曜(1営業日経過)

クレジットカード決済が完了した旨のメールが届く

第1週 水曜(2営業日経過)

商品が発送された旨のメールが届く

第1週 木曜(3営業日経過)

商品が到着(gooのスマホ+OCNモバイルONEの申込書)

申込書に従い、OCNモバイルONEフォームアクティベーションコード等を入力して、SIM申し込み

ここからOCNモバイルONEのSIMが届くまでできること
  • Wi-Fiを使った通信
  • SMS認証を必要としないアプリ

第1週 金曜(4営業日経過)

OCNモバイルONE申込受付完了の旨のメールが届く

第2週 月曜(5営業日経過+週末2日)

OCNモバイルONEのSIMが到着

ここからOCN会員登録証が届くまでできること
  • 4G/3G通信(ただしOCNモバイルONEアプリの利用(通信量の確認等)は不可)
  • SMS認証を必要とするアプリ

第2週 水曜(7営業日経過+週末2日)

OCN会員登録証が到着

登録証に記載されたメールアカウントでメールを確認すると、050plusの番号とパスワードが記載されたメールが届いていた

ここからできること
  • OCNモバイルONEアプリ
  • 050plusアプリ
  • OCN光モバイル割申し込み(OCN光を使っていれば)