Tarantubeでメッセージキュー

# はじめに

[前回の記事](https://qiita.com/mumez/items/995dfcaba25f4d868945)から1年ほど経ちました。
[Tarantool](https://tarantool.org/)は1.7系から1.8に移り、No SQLなのに[SQLが使える](https://tarantool.org/en/doc/1.8/tutorials/sql_tutorial.html#sql-tutorial)ようになったりなど、着実に進化を続けています。

Tarantool本体はさておき、今回は[Tarantalk](https://github.com/mumez/Tarantalk)のメッセージキュー拡張、[Tarantube](http://smalltalkhub.com/#!/~MasashiUmezawa/Tarantube)の解説をしてみます。

メッセージキュー(MQ)というと[RabbitMQ](https://www.rabbitmq.com/)や、[HornetQ](http://hornetq.jboss.org/)などが有名ですが、Tarantoolのqueueはそれらに比べて軽量で、気軽に導入できるというところが特徴になっています。

なおTarantool本体の特徴については前々回の記事[「SmalltalkからLuaをevalする」](https://qiita.com/mumez/items/0a288295b9ad3550b3c2)を参考にしてください。

# インストール

## Tarantoolにqueueモジュールを入れる

まずはTarantool自体にqueueモジュールを入れる必要があります。
Tarantoolには[機能拡張のモジュール](https://tarantool.org/en/download/rocks.html)が豊富に用意されており、queueもその一つです。

入れるのは簡単で、管理用コマンドの`tarantoolctl`を使用します。

```shell
tarantoolctl rocks install queue
```

後はTarantoolのコンソールからrequireすると使えるようになります。

```lua
tarantool> box.cfg{listen = 3301}
tarantool> queue = require('queue')
tarantool> queue.stats()
```
![スクリーンショット 2017-12-21 0.17.23.png](https://qiita-image-store.s3.amazonaws.com/0/61787/9b3c422e-e482-01c6-3a27-a086a926fe0c.png)

まだ何もしていないので、`queue.stats()`でstatisticsを表示させても空の状態です。

## PharoにTarantubeを入れる

バックエンド側の準備ができたので、次にPharoにTarantubeを入れていきます。
Tarantubeは、オブジェクト指向なAPIでTarantoolのqueueが使えるようにしたものです。
といっても大げさなものではなく、わずか３つのクラスからできている軽量なライブラリです。

いつものMetacelloのスクリプトで入ります。(Playgroundで"Do it"です)

```smalltalk
Metacello new
    smalltalkhubUser: 'Pharo' project: 'MetaRepoForPharo60';
    configuration: 'Tarantube';
    version: #stable;
    get; load.
```

あるいはCatalog Browser経由でも良いでしょう。デスクトップメニューの'Tools'->'Catalog Browser'で開き、検索窓に'tube'と打ちます。Tarantubeが見つかるので、選択して右クリックメニューで'Install stable version'を選びます。

![スクリーンショット 2017-12-21 0.27.13.png](https://qiita-image-store.s3.amazonaws.com/0/61787/ed928368-b43b-51fe-fb5a-8beb9742892a.png)

# 動かしてみる

Tarantoolをqueueモジュールを組み込んだ上で起動しましょう。
以下のようなconfig.luaを用意しておくと楽です。

```lua:config.lua
box.cfg{listen = 3301}
box.schema.user.create('taran', {password = 'talk', if_not_exists=true})
box.schema.user.grant('taran', 'read,write,execute', 'universe', nil, {if_not_exists=true})
queue = require('queue')
require('console').start()
```

詳しくは省きますが、3301ポートでクライアントからの接続を待ち受けるようにし、DBのユーザとしてtaranさんを登録、queueモジュールを入れて、対話型のコンソールを開くという処理になっています。

```shell
tarantool　config.lua
```
とすると、tarantoolのコンソールが上がった状態になります。

![スクリーンショット 2017-12-21 22.41.51.png](https://qiita-image-store.s3.amazonaws.com/0/61787/a31b711b-a5f6-3a46-2c41-52ae2e06ceef.png)

# つないでみる

このTarantoolにTarantubeでつなぎ、queue機能を使ってみましょう。

```smalltalk
tarantalk := TrTarantalk connect: 'taran:talk@localhost:3301'.
tarantalk tubes. "現在のtube一覧"
```
まだ"Print it"(Cmd + p)しても空の配列```#()```が返ってくるだけです。

## FIFO キューの作成

'my_tube'という名前のキューを作成します。Tarantubeではキューのことをなぜかtubeと呼ぶのです。
"Do it"(Cmd + d)してみます。

```smalltalk
"FIFOのキューを作成"
tube := tarantalk ensureTubeNamed: 'my_tube'.
```

```tarantalk tubes```を、再び"Print it"すると、今度は```an Array(TrTube( id: 0 name: my_tube type: fifo))```となりました。

## キューにタスクを追加

キューにタスクを積んでみます。MessagePackでエンコードできる範囲で、任意のオブジェクトをキューに追加できるのですが、今回は簡単のため、文字列オブジェクトを入れていきます。

```smalltalk
"キューにタスクを追加"
1 to: 10 do:  [:idx | tube putTaskWith: ('タスク ', idx asString)]. 
```

"Do it"後、おもむろにTarantoolのコンソールに戻って、```queue.stats()```してみましょう。

![スクリーンショット 2017-12-21 22.57.23.png](https://qiita-image-store.s3.amazonaws.com/0/61787/919c341a-1b4e-b60e-1364-c6ddcaf96efd.png)

putされたタスクが10件あるということがわかります。このタスクをluaから取り出しても良いですし、別のPharoイメージから取り出してもよいのです。永続化されているので、Tarantoolを落としても、キューは次回の起動で復活します。

## キューからタスクを取り出す

タスクを取り出してみましょう。本来は別のPharoイメージでやったほうが気分が出ますが、ここでは上記のtubeをそのまま使います。

結果を見るため、"Do it"前にトランスクリプトを開けておいてください。
デスクトップメニューで'Tools'->'Transcript'です。

```smalltalk
"定期的にタスクを処理(タイムアウトとして２秒を指定)"
tube repeatTakeTaskFor: 2 ifAvailable: [:task | Transcript cr; show: {task. task data}. task done].
```
実行するとこんな感じで表示されます。

![スクリーンショット 2017-12-21 23.05.54.png](https://qiita-image-store.s3.amazonaws.com/0/61787/6c54fb20-e44f-c8f5-524f-03bc53561fae.png)

今後はstatisticsをPharo側から見てみましょう。"Inspect it"(Cmd + i)します。

```smalltalk
tube statistics.
```
インスペクタが上がります。'calls'を辿ってみると、取得('take')が10回行われて、すべて無事完了('ack')したことがわかります。

![スクリーンショット 2017-12-21 23.33.25.png](https://qiita-image-store.s3.amazonaws.com/0/61787/8f971155-a169-c12b-5ee5-608055ba7a87.png)


当然ながら、Tarantoolのコンソールの```queue.stats()```の結果と同じになっています。

![スクリーンショット 2017-12-21 23.09.35.png](https://qiita-image-store.s3.amazonaws.com/0/61787/6ce4c9d0-618e-726c-52fc-cd50741147ca.png)

非同期にタスクを積んでおいて、好きなタイミングで取り出して実行できるということが確認できました。

# 何に使っているのか?

Tarantubeは[ALLSTOCKER](https://allstocker.com/)という中古建設機械売買プラットフォームで、注文メールの送信などに使われています。Pharoで非同期に処理したいときは、なかなか役に立つやつなのです。

# まとめ

Tarantubeの基本的な使い方を解説しました。Tarantubeについては、タスクの優先度や、ディレイやサブキューの指定など、まだまだ面白い機能があるのですが、それらについては**26日の[Smalltalk忘年会2017](https://smalltalk.connpass.com/event/74517/)でお話する予定**です。こちらもお楽しみに。