crossbar.ioのインストールとサンプルプログラムの動かし方(2018年9月3日版)

オープンソースのWAMPルーター[crossbar.io](https://crossbar.io/)のインストール方法とサンプル設定、WAMPクライアントの動かし方をご紹介します。

crossbar.ioの公式サイト上にもドキュメントはひと通り揃っているのですが、プログラムのアップデートに対してドキュメントの更新が追いついていません。現状Get Startedですら書かれている通り実行できない状況です。せっかくWAMPに興味を持った人がこんなところで躓いて諦めてしまうというのはあまりにも残念なので、とりあえず最新のプログラムを使ってサンプルを動かすところまでをご紹介します。

なおcrossbar.ioというのはソフトウェアの名前であると同時にそれを作ってる会社の名前でもあります。まぎらわしいので以下ソフトの方をcrossbar、会社の方をcrossbar.ioと表記します。

「いったいWAMPって何なの？」という人は[こちらに簡単な説明](https://docs.google.com/presentation/d/1pRZfjE2vpd4vdNvqp5vJYGep6BBmM-KDnBs_wS7N6Zk/edit?usp=sharing)を置いておきましたので参考にしてください。

## crossbarのインストール

crossbarには各プラットフォーム用の公式dockerイメージが用意されており、プロダクション環境ではそれらを使うことが強く推奨されていますが、起動オプションを自由に変更できないため開発用途には向きません。以下に紹介するのは開発用の環境構築手順です。

なおcrossbarはPythonで書かれています。Pythonはasync/await構文をサポートした3.5以降のバージョンを使用してください。また解説で使用しているcrossbarのバージョンは18.7.2です。

### Linux　(UbuntuおよびRaspbian)

Ubuntu、Raspbian何れの場合も同じ手順でインストール可能です。事前にCコンパイラやヘッダファイルをインストール、venvでPythonの仮想環境を有効にした後、pipでcrossbarをインストールします。

```
$ sudo apt-get install build-essential python3-dev
$ mkdir crossbar
$ cd crossbar/
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install wheel
$ pip install crossbar
```
### Windows

crossbarが依存するパッケージのひとつ[Twisted](https://twistedmatrix.com/)は、Python 3.7.0との組み合わせでpipインストールするとエラーが発生してしまいます。とりあえずcrossbarを動かすにはPython 3.6.xを使ってください。また、twistedはcrossbarをインストールする前にwindows_platformを指定してインストールしておきます。

```
mkdir crossbar
cd crossbar
python -m venv venv
venv\Scripts\activate.bat
pip install Twisted[windows_platform]
pip install crossbar
```

### Mac

crossbarのバージョン18.7.xはMac上で動かすとpsutilまわりでエラーが発生するのですが、[対応されるまでしばらく時間がかかりそうな様子](https://github.com/crossbario/crossbar/issues/1333)です。[パッチを当てたforkをgithubで公開している人がいるので](https://github.com/jaimewyant/crossbar)、これを利用してインストールします。

```
$ mkdir crossbar
$ cd crossbar/
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install wheel
$ pip install "git+https://github.com/jaimewyant/crossbar.git"

```

### 動作確認　(各プラットフォーム共通)

`crossbar version`を実行します。以下のようなメッセージが表示されたらインストールは完了です。

```
 crossbar version

    :::::::::::::::::
          :::::          _____                      __
    :::::   :   :::::   / ___/____ ___   ___  ___  / /  ___ _ ____
    :::::::   :::::::  / /__ / __// _ \ (_-< (_-< / _ \/ _ `// __/
    :::::   :   :::::  \___//_/   \___//___//___//_.__/\_,_//_/
          :::::
    :::::::::::::::::   Crossbar v18.7.1

    Copyright (c) 2013-2018 Crossbar.io Technologies GmbH, licensed under AGPL 3.0.

 Crossbar.io        : 18.7.1
   Autobahn         : 18.8.2
   Twisted          : 18.7.0-KQueueReactor
   LMDB             : 0.94/lmdb-0.9.22
   Python           : 3.7.0/CPython
 Frozen executable  : no
 Operating system   : Darwin-17.7.0-x86_64-i386-64bit
 Host machine       : x86_64
 Release key        : RWS9T4NltFjmKSMbEtETnOMxRjLhOEZ6e80T5MYzTTh/+NP9Jk20sJmA
```

テスト用のディレクトリを作成して`crossbar init`を実行してください。必要なサブディレクトリと設定ファイルの雛形が作成されます。`.crossbar/config.json`がcrossbarのノード設定ファイルです。

```
$ mkdir testnode
$ cd testnode
$ crossbar init
Initializing node in directory '/Users/yasusii/crossbar/testnode'
Using template from '/Users/yasusii/crossbar/venv/lib/python3.7/site-packages/crossbar/node/templates/default'
Creating directory /Users/yasusii/crossbar/testnode/.crossbar
Creating directory /Users/yasusii/crossbar/testnode/web
Creating file /Users/yasusii/crossbar/testnode/README.md
Creating file /Users/yasusii/crossbar/testnode/.crossbar/config.json
Creating file /Users/yasusii/crossbar/testnode/web/README.md
Application template initialized

To start your node, run 'crossbar start --cbdir /Users/yasusii/crossbar/testnode/.crossbar'
```
続いて`crossbar start`を実行してノードを立ち上げます。オプション`--cbdir`には設定ファイルが置かれた`.crossbar`ディレクトリを指定します。このオプションが省略された場合はカレントディレクトリ下に`.crossbar`があるものして動作します。

```
$ crossbar start --cbdir=./.crossbar
2018-09-02T22:15:24+0900 [Controller   1495]
2018-09-02T22:15:24+0900 [Controller   1495]     :::::::::::::::::
2018-09-02T22:15:24+0900 [Controller   1495]           :::::          _____                      __
2018-09-02T22:15:24+0900 [Controller   1495]     :::::   :   :::::   / ___/____ ___   ___  ___  / /  ___ _ ____
2018-09-02T22:15:24+0900 [Controller   1495]     :::::::   :::::::  / /__ / __// _ \ (_-< (_-< / _ \/ _ `// __/
2018-09-02T22:15:24+0900 [Controller   1495]     :::::   :   :::::  \___//_/   \___//___//___//_.__/\_,_//_/
2018-09-02T22:15:24+0900 [Controller   1495]           :::::
2018-09-02T22:15:24+0900 [Controller   1495]     :::::::::::::::::   Crossbar v18.7.1
2018-09-02T22:15:24+0900 [Controller   1495]
2018-09-02T22:15:24+0900 [Controller   1495]     Copyright (c) 2013-2018 Crossbar.io Technologies GmbH, licensed under AGPL 3.0.
2018-09-02T22:15:24+0900 [Controller   1495]
2018-09-02T22:15:24+0900 [Controller   1495] Initializing <class 'crossbar.personality.Personality'> node from node directory "/Users/yasusii/crossbar/testnode/.crossbar"
2018-09-02T22:15:24+0900 [Controller   1495] Node key loaded from "/Users/yasusii/crossbar/testnode/.crossbar/key.priv"
2018-09-02T22:15:24+0900 [Controller   1495] Node configuration loaded from "/Users/yasusii/crossbar/testnode/.crossbar/config.json"
2018-09-02T22:15:24+0900 [Controller   1495] Entering event reactor ...
2018-09-02T22:15:24+0900 [Controller   1495] Starting standalone node
2018-09-02T22:15:24+0900 [Controller   1495] Node ID "yasushi-no-mac-mini.local" set from hostname
2018-09-02T22:15:24+0900 [Controller   1495] No extra node router roles
2018-09-02T22:15:24+0900 [Controller   1495] RouterServiceAgent ready (realm_name="crossbar", on_ready=None)
2018-09-02T22:15:24+0900 [Controller   1495] Registered 21 procedures
2018-09-02T22:15:24+0900 [Controller   1495] Signal handler installed on process 1495 thread 140735583114112
2018-09-02T22:15:24+0900 [Controller   1495] Using default node shutdown triggers ['shutdown_on_worker_exit']
2018-09-02T22:15:24+0900 [Controller   1495] Booting node
2018-09-02T22:15:24+0900 [Controller   1495] Configuring node from local configuration
2018-09-02T22:15:24+0900 [Controller   1495] Starting 1 workers ...
2018-09-02T22:15:24+0900 [Controller   1495] Starting router worker "worker-001"
2018-09-02T22:15:24+0900 [Controller   1495] Starting new managed worker process for Router worker "worker-001"
2018-09-02T22:15:25+0900 [Router       1498] Starting worker "worker-001" for node "yasushi-no-mac-mini.local" with personality "standalone"
2018-09-02T22:15:25+0900 [Router       1498] Running as PID 1498 on CPython-KQueueReactor
2018-09-02T22:15:25+0900 [Router       1498] Entering event reactor ...
2018-09-02T22:15:25+0900 [Router       1498] Router worker session for "worker-001" joined realm "crossbar" on node router
2018-09-02T22:15:25+0900 [Router       1498] Registered 41 procedures
2018-09-02T22:15:25+0900 [Router       1498] Router worker session for "worker-001" ready
2018-09-02T22:15:25+0900 [Controller   1495] Router worker "worker-001" process 1498 started
2018-09-02T22:15:25+0900 [Router       1498] Starting router realm "realm-001"
2018-09-02T22:15:25+0900 [Router       1498] RouterServiceAgent ready (realm_name="realm1", on_ready=<Deferred at 0x107cea7b8>)
2018-09-02T22:15:25+0900 [Router       1498] Realm "realm-001" (name="realm1") started
2018-09-02T22:15:25+0900 [Controller   1495] Router "worker-001": realm 'realm-001' (named 'realm1') started
2018-09-02T22:15:25+0900 [Router       1498] Starting role "role-001" on realm "realm-001"
2018-09-02T22:15:25+0900 [Router       1498] role role-001 on realm realm-001 started
2018-09-02T22:15:25+0900 [Controller   1495] Router "worker-001": role 'role-001' (named 'anonymous') started on realm 'realm-001'
2018-09-02T22:15:25+0900 [Router       1498] Starting router transport "transport-001"
2018-09-02T22:15:25+0900 [Router       1498] Creating router transport for "transport-001"
2018-09-02T22:15:25+0900 [Router       1498] Router transport created for "transport-001"
2018-09-02T22:15:25+0900 [Router       1498] UniSocketServerFactory starting on 8080
2018-09-02T22:15:25+0900 [Controller   1495] Router "worker-001": transport 'transport-001' started
2018-09-02T22:15:25+0900 [Router       1498] Starting "nodeinfo" Web service on path "info" of transport "transport-001"
2018-09-02T22:15:25+0900 [Controller   1495] Router "worker-001": web service 'nodeinfo' started on path 'info' on transport 'transport-001'
2018-09-02T22:15:25+0900 [Controller   1495] Local node configuration applied successfully!
```
設定ファイルが正しく読み込まれ、起動に成功すると最後に`Local node configuration applied successfully!`と出力されます。

crossbarにはWebサーバーが組み込まれています。ブラウザで`http://localhost:8080/info/`にアクセスするとバージョン情報のページが表示されます。

![crossbar_web.png](https://qiita-image-store.s3.amazonaws.com/0/285004/23b3ab89-a783-fbb4-fca6-2c3268abd4eb.png)

確認が終わったら一旦`Ctrl-C`でcrossbarを終了してください。以上でWAMPルーターcrossbarのインストールは完了です。

## クライアント用ライブラリAutobahn(アウトバーン)

WAMPアプリケーションは複数のWAMPクライアント間の相互通信によって成り立ちます。WAMPルーターはこの通信を仲介するものであり、アプリケーションの主役はクライアントの方になります。crossbar.ioはWAMPクライアント開発用のライブラリとして[Autobahn](https://crossbar.io/autobahn/)を提供しています。こちらもオープンソースです。Python、JavaScript、C、C++、Java用が用意されています。

ほかにサードパーティのWAMPライブラリが数多くあります。以下のサイトに一覧が掲載されているので、開発に上記以外の言語を使いたい場合は参考にしてください。

https://wamp-proto.org/implementations/index.html

なおcrossbar自体がAutobahn Pythonを使って書かれているため、crossbarをインストールするだけでAutobahn Pythonも自動的に使えるようになります。crossbarと同じPython venv環境を使う場合、別途インストールの必要はありません。

## サンプルプログラムの実行

WAMPルータの設定とWAMPクライアント・プログラムのサンプルは以下のgitリポジトリに置かれているので、これをダウンロードします。

https://github.com/crossbario/crossbar-examples

各言語毎に様々なサンプルが用意されていますが、最も基本的なサンプル`crossbar-examples/hello/python/`を実行してみます。これはPythonで実装したクライアントとWebブラウザ上で動くJavaScriptクライアント間で、PubSubおよびRPCの簡単なやり取りを行う例です。

Webブラウザ側で動かすAutobahnJSもダウンロードしておきましょう。

https://github.com/crossbario/autobahn-js-browser

使用するファイルは`autobahn-js-browser/autobahn.min.js`です。このファイルを`crossbar-examples/hello/python/web`にサブディレクトリshared/autobahnを作成し、そこにコピーしてください。サンプルのファイル構成は次のようになります。

```
$ mkdir -p crossbar-examples/hello/python/web/shared/aut
obahn
$ cp autobahn-js-browser/autobahn.min.js crossbar-exampl
es/hello/python/web/shared/autobahn
$ cd crossbar-examples/hello/python
$ tree -a
.
├── .crossbar
│   └── config.json # crossbarの設定ファイル
├── README.md
├── hello.py # PythonのWAMPクライアント
└── web
    ├── index.html # WAMPクライアントのJavaScriptを含むhtml
    └── shared
        └── autobahn
            └── autobahn.min.js　# AutobahnJSライブラリ

4 directories, 5 files
```

準備ができたらcrossbarを起動します。Pythonクライアント・プログラムhello.pyは設定ファイルconfig.jsonでコンポーネントとして記述されているため、crossbarに組み込まれた形で自動起動します。

```
$ pwd
/Users/yasusii/github/crossbar-examples/hello/python
$ crossbar start
```
このhelloサンプルは次の4つの内容をそれぞれ1秒置きに実行します。

- PubSubトピックcom.example.onhelloにPythonクライアントが数値をインクリメントしながら送信、JavaScriptクライアント側で受信
- 同じトピックにJavaScriptクライアントがメッセージ'Hello'を送信、Pythonクライアント側で受信
- 2つの引数の加算結果を返すプロシージャadd2()をPythonクライアントが登録、JavaScriptクライアントから実行
- ２つの引数の乗算結果を返すプロシージャmul2()をJavaScriptクライアントが登録、Pythonクライアントから実行

crossbarの起動直後、コンソールには次のようにプロシージャadd2()の登録とPubSubトピックにカウンタの数値をpublishしている様子が繰り返し出力されます。

```
2018-09-03T22:08:38+0900 [Container    2103] procedure add2() registered
2018-09-03T22:08:38+0900 [Container    2103] published to 'oncounter' with counter 0
2018-09-03T22:08:39+0900 [Container    2103] published to 'oncounter' with counter 1
2018-09-03T22:08:40+0900 [Container    2103] published to 'oncounter' with count

```

続いてWebブラウザで`http://localhost:8080/`にアクセス、JavaScriptクライアントを接続します。これでPythonクライアントとJavaScriptクライアント間の通信が開始されます。ブラウザのJavaScriptコンソールを確認してみましょう。次の4種類の内容が出力されているはずです。

- `on_counter() event received with counter n`: Pythonクライアントから受信したPubSubメッセージ
- `published to topic 'com.example.onhello'`: PubSubメッセージの送信
- `add2() result: n`: Pythonクライアントから返ってきたプロシージャの実行結果
- `mul2() called with n1 and n2`: Pythonクライアントへのプロシージャ呼び出し

![crossbar_hello_console.png](https://qiita-image-store.s3.amazonaws.com/0/285004/6575dabf-86e2-1ac4-4dc1-c10ee227dcf9.png)

ブラウザ側を確認したらもう一度crossbar側のコンソールに戻って見てみましょう。ログに次の内容が加わっているはずです。

- `event for 'onhello' received: Hello from JavaScript (browser)`: JavaScriptクライアントから受信したPubSubメッセージ
- `add2() called with n1 and n2`: JavaScriptクライアントへのプロシージャ呼び出し
- `mul2() called with result: n`: JavaScriptクライアントから返ってきたプロシージャの実行結果

これらがどのようにプログラムとして書かれているのかはそれぞれのプログラムと設定ファイルを参照してください。

ここまで来れば、ほかのサンプルも実行し内容を確認できるはずです。