テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(msgpack-rpc-nim report) RPC疎通完了

akiradeveloper/msgpack-rpc-nim · GitHub

とりあえず, サーバ側で「intを受け取ってその倍を返す」という関数を置いて, クライアントから100を投げて200を受けるというテストは通った. デバグで苦戦したのでプリント文が見苦しいが, 以下のコードが動作する.

hen isMainModule:
  let
    addrUse = "127.0.0.1"
    portUse = Port(20001)
  let cl = commandLineParams()
  assert(len(cl) == 1)
  let t = cl[0] # server or client
  if t == "server":
    # Note:
    # We need unbuffered sockets for both client and server.
    # The reason is not found.
    let sock = newAsyncSocket(buffered=false)
    defer: sock.close()
    sock.bindAddr(address=addrUse, port=portUse)
    sock.listen()
    var server = mkServer(sock)
    server.addMethod("double", proc (args: openArray[Msg]): Msg =
      let a = unwrapInt(args[0])
      wrap(a * 2))
    asyncCheck server.run
    runForever()
  elif t == "client":
    let sock = newAsyncSocket(buffered=false)
    defer: sock.close()
    let client = mkClient(sock)
    waitFor sock.connect(address=addrUse, port=portUse)
    echo "call start"
    let fut1 = client.call(id=0, "double", @[PFixNum(100)])
    echo "call end"
    let ret1: Msg = waitFor(fut1)
    echo "ret: ", unwrapInt(ret1)
  else:
    assert(false)

msgpack-rpc-nimが提供する実装は, 「未使用のAsyncSocketが手に入った」ことを前提にしたコードである. つまり, Clientがcallを連発するためには別々の未使用AsyncSocketが必要となる. AsyncSocketを提供するasyncnetモジュールは非同期ソケット通信を提供するNimの公式モジュールであり, 今回の用途にピッタリなので採用した. AsyncSocketは, 「それがどういうプロトコルで繋がっているか」を隠蔽してくれるため, 私の側ではこの点を一切考慮しなくて済む利点がある. つまり, 上のコードはAsyncSocketの生成法やbindの方法を変更すれば, UNIX domain socketでも動作する(と思う. はずである). これは, MessagePack-RPCの要件である.

AsyncSocketの生成メソッドは以下. 何でも吸収してくれそうなことが分かる.

proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
    protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =

callは, Future型を返す. これを具体的にwaitForすることによってcompletionをpollして待つ. これは, Nimなどのまともな言語では当然の実装と言える.

性能の面からは, 呼び出し毎にコネクションを張るオーバーヘッドは嫌われる. したがって実用上, これをプールするとかいうコードは別に必要となるが, これは今まで作った実装の上に作ることが出来る. 具体的には, 「生成方法を内包したプール」が必要になるだろう. これは, mempoolと同様の考え方である. ただし, futureのcompletionかどこかで, そのAsyncSocketがフリーになったことをマークする方法(release)が必要となる. つまり一般化すると, alloc/dealloc/acquire/releaseを実装するプールがあれば良く, これで一つのライブラリ化が出来る. 早速プロトタイプしようと思う.

このテストコードでは, AsyncSocketがbuffered=falseで生成されているが, server/clientともに, unbufferedでなければ動作しなかった. 私の感覚では, buffferingをon/offしたところで挙動が変わるのはおかしいと思うので, ライブラリのバグを疑うが, もしかしたらそういうものなのかも知れない. いずれにしろ, 今のところはドキュメントし, 仮にbufferingが有効なsocketが渡された場合にはエラーで落とすべきだと思う.

とりあえずはてなはnimコードのハイライトをサポートして欲しい. nimを使ってないのだろうか.