Akka-remoteを利用して、NATを越えたメッセージングを実現するための方法。
Akka-remoteにおいてホスト名を指定するakka.remote.netty.tcp.hostname
には、マシンが認識する名前であるプライベートアドレスもしくはFQDNしか指定することができず、グローバルアドレスを指定することができない(註)。
そしてAkka-remoteはhostname
とport
が合致しなければメッセージに応答しない。
結果としてグローバルアドレスを指定した12.34.56.78
宛の通信を無視してしまう。hostname
にWANアドレスを指定しても、インターフェースにbind
することができないので回線を開けず失敗し、こんなエラーを吐く。
Caused by: java.net.UnknownHostException: GLO.BAL.ADD.RESS: nodename nor servname provided, or not known
このためルータ越しの通信ができない。
これを解決する方法の目処が付いたので報告する。
註: グローバルアドレスがホスト名として指定されており、マシンがそれを正しく認識する場合はこの限りでない。
NATed環境下での通信路の確保
- application.confもしくはreference.confで
akka.remote.netty.tcp.use-passive-connections = on
と設定する。
use-passive-connections
をon
にすると、inbound時に張られた接続をoutbound時に再利用するようになる。これにより、Akka-remoteはhostname
の違いを無視し、hostname
にプライベートアドレスを指定した場合でもグローバルアドレスからのアクセスを受ける事が可能となる。
UPnPを利用した動的なポートマッピング
- Scalaから利用できるuPnPライブラリを使う。
グローバルアドレスからの通信を受け付ける状態でも、ポートフォワードの為にルータ上の静的なNATテーブルを逐一編集するのは不便である。 この不便はuPnPによるポートマッピングで解決できる。これはLAN内部からNAT-Port Mapping Protocol(NAT-PMP)を利用することで実現できる。しかしこれを手動で行うのも手間だし不便だ。 そして便利なことに、Java/ScalaからuPnPにアクセスするライブラリが存在している。 これを利用してアプリケーションの起動時に動的にポートを解放させることで、簡単にAkka-remoteがリモートのノードと接続できるようになる。
Scalaから利用できるuPnPライブラリ
雑記
これを発見するまでに非常に長い時間がかかったし、これが全く思惑通りに動く保障もないので、心配なことしきりです。 分散処理って難しい。
あと早くIPv6普及しろ。