2014年4月11日金曜日

[scala][akka] microkernelを使って独立したプロセスに立てたRemote Actorとの通信サンプル

自分は、akkaの「Let it crash」というという発想が大好きなんです。
初めて「Let it crash」というスローガンを見たとき
{だよね。それでいいんだよね。}
と感激しました。

ただ、crashさせるためのSupervisor(doc、サンプル)の仕組みは、Actorはcrashさせるけど、jvmまでは殺してくれない。自分の場合、ヘビーな計算をscalaからloadLibraryしたc++のモジュールをJNA経由で使ってやっているので、jvmごとcrashさせたいんです。(外部libraryのunloadはできないようなので・・・)

そこで、登場するのがakkaのmicrokernelで、こいつを使うと簡単に独立したプロセスのjvmでActorを立てることが出来る。
必要に時は、こいつをjvmごとkillして復活させればいい ← もっとスマートな仕組みがきっとある気がする

で、HelloWorldのサンプルを書いてみた。
listenポートを可変にしたかったので、confファイルを使わない形で作ってみた。
application.confとかに設定が分かれてないほうがサンプルとしてもわかりやすいし。
まず、リモートで接続される側のmicrokernel
パラメータは、起動時に引数として渡せないので、起動する時は、
env AKKA_PORT=12345 akka hello.world.Sample.HelloLauncher
て感じで、環境変数経由にする。
あと、実行時にクラスが見つかんないみたいに怒られた時は、とりあえずjarに固めて、akkaのdeployフォルダに置けば見つかるようになります。
package hello.world.Sample

import akka.actor.{ Actor, ActorSystem, Props }
import akka.kernel.Bootable

class HelloLauncher extends Bootable {
    val port = System.getenv("AKKA_PORT")

    val conf = ConfigFactory.parseString(s"""
      akka.actor.provider = akka.remote.RemoteActorRefProvider
      akka.remote.netty.tcp.hostname = 127.0.0.1
      akka.remote.netty.tcp.port = ${port}
    """.stripMargin).withFallback(ConfigFactory.load());    
    val system = ActorSystem("HelloSystem", conf)

    def startup = {
        system.actorOf(Props[Hello], "hello")
    }

    def shutdown = {
        system.shutdown()
    }  
}

class Hello extends Actor {
  def receive = {
    case msg: String =>
      println("Hello: msg = " + msg)
  }
}
つぎに、Helloにメッセージを投げる側のサンプル
env AKKA_PORT=12345 akka hello.world.Sample.HelloLauncher
env AKKA_PORT=23456 akka hello.world.Sample.HelloLauncher
のように、別コンソールで2つのmicrokernelを起動した前提で動作させる。
object HelloCaller {

import akka.actor.{ Actor, ActorSystem, Props }
    
  def main(args: Array[String]) {
    System.setProperty("akka.actor.provider", "akka.remote.RemoteActorRefProvider")
    // このポートはリモート側でsenderにメッセージを戻す時に使用する
    System.setProperty("akka.remote.netty.tcp.port", "10000")
    val system = ActorSystem("CallerSystem")
    val actor1 = system.actorSelection("akka.tcp://HelloSystem@127.0.0.1:12345/user/hello")
    val actor2 = system.actorSelection("akka.tcp://HelloSystem@127.0.0.1:23456/user/hello")
    actor1 ! "Hello,"
    actor2 ! "Hello,"
    Thread.sleep(1000)
    actor1 ! "World."
    actor2 ! "Another actor."
    Thread.sleep(1000)
    actor1 ! "Yeaaahhh!!!"
    actor2 ! "Hooooooo!!!"
    Thread.sleep(1000)
    system.shutdown
  }

}
なお、mainから呼ぶ側のlibraryDependenciesには、akka-kernel, akka-actor, akka-remote を追加しています。

[sacla] akkaのmicrokernelに引数を渡す

akkaのmicrokernelは、Actorを別プロセスで簡単にバンバン立てられるので、便利なんですけど、今のバージョン(akka 2.3.2)だと、引数を渡せないので困った。

自分はlinuxのコマンドに不慣れで、解決法を忘れそうなのでメモ。

まず、akkaのbinにパスが通っているとして、普通のmicrokernelの起動は
akka com.xxx.packageName.ClassName
でやるんだけど、引数を渡したい時に、
akka com.xxx.packageName.ClassName arg1 arg2
とかやっても、Bootableにargsを渡してくれない。(そういうインターフェースになっていない)
なんでかというと、akkaコマンドが、複数のパラメータが
akka Class1 Class2 Class3
なんてあったときに、3つのBootableを一気に立ち上げるというふうに使っているから(参照)っぽい。

で、解決としては、linuxのenvコマンド(環境を変更してプログラムを実行する)を使用してmicrokernelを
env AHO=xxx BAKA=yyy akka com.xxx.packageName.ClassName
のように起動し、Bootableの中で
  val aho = System.getenv("AHO")
  val baka = System.getenv("BAKA")
とやって引数を取得しました。

2014年2月28日金曜日

[scala] PlayFrameworkでJson文字列の取得で一瞬あせった

Play 使っててJsonの文字列取りたい時、
val jsonObj = ........ とかなんとかあった場合
ドキュメントとか世のブログとかでは、
Json.stringify(jsonObj)
とやれとのことなんだけど、何も考えずに
jsonObj.toString
ってやってて、後で、これでいいの?だめなの?
と疑問に思った。
ちょっと調べたらどうやらjavascriptでは違うらしいので、一瞬
{げっっ!!}
となったが、playのソース(JsValue)を見ると

override def toString = Json.stringify(this)

だってさ。取れるものは同じということで、安心したぜ。

2014年2月14日金曜日

[Java] マイナスの16進数文字列をLongに戻したい

javaで、マイナスの16進数文字列を数値に戻したい。ただそれだけなのに、はまってしまった。信じられん。ということで、運良く解決したのでメモ。
これ、絶対はまるやつ結構いると思うんだけどなぁ。javaめ。

まず、説明のため順序的にLong->16進数ね。
Long l = new Long(-1);
String s = Long.toHexString(l);
これ、当然sが"ffffffffffffffff"。 fが16個。これが欲しいのでOK。
で、今回のお題は、16進数->Longなので、とりあえずこうするわね。
Long rev = Long.parseLong(s, 16);
しかし、ここで実行時エラー。
{なぬーっ。そんなアホなーーっ。}
どうやら、parseLongは、sの中身を正の値だと決めてかかっているようで、"7fffffffffffffff"より大きい文字列を入れるとエラーになる仕様。
Long.decodeも試してみたが、同仕様。
{どうしよう。とほほ。}
ちなみに、
Long ll = Long.parseLong("-ff", 16);
とやると、-255になるんだそうな。誰が喜ぶんだそんな仕様・・・
で、なぜかいろいろググっても解決できなかったのだが、コードをごねごねいじってたらラッキーにも打開策を発見した。
答えは、
Long rev = (new java.math.BigInteger(s, 16)).longValue();
一度、BigIntegerにしてから、それをLongにする。見事revが-1になった。
ナイス、BigInteger.longValue()!!
こいつの仕様に助けられた。
あとは、勝手にlongValue()の仕様が変らないことを祈るだけだ。

2014年1月29日水曜日

L2TP over IPsec (UDP4500) のポート開放が効かなくて困った。解決メモ。

ローカルネットワーク内部に、SoftEther等のL2TP over IPsecを使用するVPNサーバーを置いて、iPhoneやmacやAndroidからVPN接続をしようとしていたのだが、どうもうまくいかなかった。

ルーターにポートフォーディングの設定を正しくして、ルーターを再起動すると「ちゃんと外部からVPN接続できる。」のだが、しばらくするとL2TP over IPsecのポートフォーディングが動かなくなる。
他のポートのポートフォーディングは、ちゃんと動いていて、ダメになるのはL2TP over IPsecのポートだけ。

ルーターを再起動すると復活するが、1日から1週間位運用していると、またダメになる。いつダメになるかは読めない。

という変な状況が続いていた。ルーターを再起動すると直るので、当初ルーターを疑ったのだが、真の原因が判明したので、メモっておく。

原因:ローカルネットワーク内部に存在するMacBookの「どこでもMy Mac」機能がUPnPを使って、ルーターのUDP4500を奪っていたので、VPNサーバーにUDP4500が転送されていなかった。

対処:ルーターのUPnP機能をOFFにした。

今まで知らなかったのだが、UPnPというのは、ルーターのポートフォワードテーブルを勝手に書き換える(詳しくはここ)という恐ろしいやつで、うちで使っているルーター(PR-S300SE)では、手動で設定したポートフォワードテーブルよりも優先度が高いため、ポートが奪われてしまっていたようだ。

ルーター再起動でVPNサーバーにつながるようになっていたのは、再起動でルーターのUPnPテーブルがクリアされるからだった。

PR-S300SEに限らず、UPnPがデフォルトでONになっているルーターはあるようなので、似たような症状の出ている人はチェックするといいかもしれません。

2013年12月10日火曜日

CentOS6.2にgcc4.8.2を入れた時のメモ

CentOS6.2のgcc4.7.2が入っている環境に、gcc4.8.2を入れた時、最後のgccのmakeではまったので記録。

まず、gcc4.8のコンパイルについては超親切で丁寧なページがあるので、それを参考に進めるのだが、最後のgccのmakeで少しはまったので、解決した時のconfigureオプションを残しておきます。

./configure --with-gmp=/usr/local --with-mpfr=/usr/local --with-mpc=/usr/local --with-ppl=/usr/local --with-cloog=/usr/local --program-suffix=-4.8 --enable-languages=c,c++ --disable-multilib

gccをc/c++しか使わない人用です。
なぜか、各コンポーネントを見つけてこないことがあるので全部ディレクトリ指定しました。
あと、64bit環境しかいらないので、--disable-multilib を指定しています。

2013年11月25日月曜日

[c++11] えっc++って__METHOD__ないの?

プログラム書いてて、エラーメッセージのところで発生箇所のメソッド名を付けたかったんだけど、よくわからんから何となく __METHOD__ とか書いてみたら当然エラーになった。
__func__じゃ短いし、__PRETTY_FUNCTION__じゃ長いので、
// class::method みたいな文字列を取るマクロ
#define __METHOD__ [](std::string s){int sp=s.find(' ')+1;int pa=s.find('(');if(sp>pa)sp=0;return s.substr(sp,pa-sp);}(__PRETTY_FUNCTION__)
ってやったら欲しいのができた。
type_info::name()を使ってもよかったんだけど、デマングルとか面倒くさそうだったので、上の形にした。
自分でよく使いそうなのでメモ。