Open11

NNG (NanoMsg NewGeneration)メモ

ktz_aliasktz_alias

NNGとは

公式サイト
https://nng.nanomsg.org/
公式レポジトリ
https://github.com/nanomsg/nng

特徴

  • zeromq同様ブローカーレス
  • 先行するnanomgに対してスケーラビリティを追求したもの
  • zeromqnanomgとは異なり、プロトコルごとに関数セットを分割している
    • 新しいプロトコルは、新しい関数セットを追加するだけ
    • APIの複雑さとのトレードオフ
ktz_aliasktz_alias

コンテキスト

  • 1つのソケットを複数のスコープに分け、並行的に通信する考え
    • zeromqのコンテキストとは考え方が違うことに注意
    • rawモードではサポートされない
  • 一つのREPソケットで複数のREQを受信に使われる
    • リクエストは非同期IOで受ける
    • zeromqではポーリングで実現してたやつ

通常

  • サーバ側の受信は1リクエスト複数コンテキスト
  • クライアント側は1リクエスト1コンテキスト

が無難

ktz_aliasktz_alias

トランスポートレイヤー

nngのトランスポートはURLを用いてzeromqに類似した記述を行う。

  • inproc (inproc://<IDENTIFIRE>)

    • 同一プロセス内での通信
    • スレッド間をシェアードナッシングに保つのに役立つ
    • ゼロコピー
    • 別アプリケーションで同じURLを使用しても干渉しない
  • ipc (ipc://<PATH>)

    • 同一ホスト内の別プロセス間通信
    • UNIXプラットホームではPOSIXドメインソケットを使用
    • Windows環境ではPATHとしてUNC名前付きパイプを使用する
  • BSDソケット (socket://...)

    • 実体はTCPソケット
  • TCP (tcp://<IP Address:PORT> or tcp://HOSTNAME:PORT)

    • TCP/IPネットワーク間で通信
    • IPv4とIPv6をサポート
      • IPv6の場合はtcp://[::1]:80のようにブラケットで囲む
      • リスナーURLは、tcp://0.0.0.0:80tcp://*:80な感じ
  • TLS (tls+tcp://<IP Address:PORT> or tls+tcp://HOSTNAME:PORT)

    • TLS v1.2による通信
      • ネットワークトランスポートはTCP
    • mbedTLS依存
    • URLの記述はTCPに準拠する
    • 公式レポジトリに、optionally v1.3って書かれてるから、そっちもいけるのかも
      • ドキュメントには書かれてない
  • WebSocket (ws://<IP Address:PORT> or ws://HOSTNAME:PORT)

    • URLの記述はTCPに準拠する
    • URLとしてwss://も使用可能
  • ZeroTier (zt://<DIGEST>)

  • Stream

    • 任意のプロトコルに対するプロキシ的立ち位置
    • listener: nng_stream_listener_alloc関数でソケットを作成
      • url指定ならnng_stream_listener_alloc_url関数を使う
    • dealer: nng_stream_dialer_alloc関数でソケットを作成
      • url指定ならnng_stream_dialer_alloc_url関数を使う
ktz_aliasktz_alias

inprocのオプション

  • NNG_OPT_IPC_PERMISSIONS

  • NNG_OPT_IPC_SECURITY_DESCRIPTOR

  • NNG_OPT_LOCADDR

  • NNG_OPT_REMADDR

  • NNG_OPT_PEER_GID

  • NNG_OPT_PEER_PID

  • NNG_OPT_PEER_UID

  • NNG_OPT_PEER_ZONEID

  • NNG_OPT_URL

ktz_aliasktz_alias

ソケットのオプション

BSDソケット

  • NNG_OPT_PEER_GID

  • NNG_OPT_PEER_PID

  • NNG_OPT_PEER_UID

  • NNG_OPT_PEER_ZONEID

TCP

  • NNG_OPT_LOCADDR

  • NNG_OPT_REMADDR

  • NNG_OPT_TCP_KEEPALIVE

  • NNG_OPT_TCP_NODELAY

NNG_OPT_URL

TLSのオプション

  • NNG_OPT_LOCADDR

  • NNG_OPT_REMADDR

  • NNG_OPT_TCP_KEEPALIVE

  • NNG_OPT_TCP_NODELAY

  • NNG_OPT_TLS_AUTH_MODE

  • NNG_OPT_TLS_CA_FILE

  • NNG_OPT_TLS_CERT_KEY_FILE

  • NNG_OPT_TLS_CONFIG

  • NNG_OPT_TLS_VERIFIED_

  • NNG_OPT_TLS_PEER_CN

  • NNG_OPT_TLS_PEER_ALT_NAMES

  • NNG_OPT_URL

ktz_aliasktz_alias

WebSocketのオプション

  • NNG_OPT_WS_REQUEST_HEADERS

    • (string) Concatenation of multiple lines terminated by CRLF sequences, that can be used to add further headers to the HTTP request sent when connecting. This option can be set on dialers, and retrieved from pipes.
  • NNG_OPT_WS_RESPONSE_HEADERS

    • (string) Concatenation of multiple lines terminated by CRLF sequences, that can be used to add further headers to the HTTP response sent when connecting. This option can be set on listeners, and retrieved from pipes.
  • NNG_OPT_WS_RECV_TEXT

    • (bool) Enable receiving of TEXT frames at the WebSocket layer. This option should only be used with the low level nng_stream API. When set, the stream will accept in-bound TEXT frames as well as BINARY frames.
    • The SP protocols (such as REQ) require BINARY frames as they pass binary protocol data. Hence this option should not be used with such protocols.
    • RFC 6455 requires that TEXT frames be discarded and the connection closed if the frame does not contain valid UTF-8 data. NNG does not perform any such validation. Applications that need to be strictly conformant should check for this themselves.
  • NNG_OPT_WS_SEND_TEXT

    • (bool) Enable sending of TEXT frames at the WebSocket layer. This option should only be used with the low level nng_stream API. When set, the stream will send TEXT frames instead of BINARY frames.
    • NNG does not check the frame data, and will attempt to send whatever the client requests. Peers that are compliant with RFC 6455 will discard TEXT frames (and break the connection) if they do not contain valid UTF-8.
  • NNG_OPT_TLS_CONFIG

    • (nng_tls_config *) The underlying TLS configuration object for wss:// endpoints. A hold is placed on the underlying configuration object before returning it. The caller should release the object with nng_tls_config_free() when it no longer needs the TLS configuration.
    • Use this option when advanced TLS configuration is required.
  • NNG_OPT_TLS_CA_FILE

    • (string) Write-only option naming a file containing certificates to use for peer validation. See nng_tls_config_ca_file() for more information.
  • NNG_OPT_TLS_CERT_KEY_FILE

    • (string) Write-only option naming a file containing the local certificate and associated private key. The private key used must be unencrypted. See nng_tls_config_own_cert() for more information.
  • NNG_OPT_TLS_AUTH_MODE

    • (int) Write-only option used to configure the authentication mode used. See nng_tls_config_auth_mode() for more details.
  • NNG_OPT_TLS_VERIFIED

    • (bool) Whether the remote peer has been properly verified using TLS authentication. May return incorrect results if peer authentication is disabled.
  • NNG_OPT_TLS_PEER_CN

    • (string) This read-only option returns the common name of the peer certificate. May return incorrect results if peer authentication is disabled.
  • NNG_OPT_TLS_PEER_ALT_NAMES

    • (string list) returns string list with the subject alternative names of the peer certificate. May return incorrect results if peer authentication is disabled.
ktz_aliasktz_alias

プロトコル

  • REQ/REP
    • req -> rep -> reqの一方向通信
    • req: nng_req0_open関数でソケットを作成する
    • rep: nng_rep0_open関数でソケットを作成する
    • コンテキストサポート
  • PUB/SUB
    • pub -> subの一方向通信
    • pub: nng_pub0_open関数でソケットを作成する
    • sub: nng_sub0_open関数でソケットを作成する
  • PUSH/PULL
    • push -> pullの一方向通信
    • push: nng_push0_open関数でソケットを作成する
    • pull: nng_pull0_open関数でソケットを作成する
  • PAIR
    • p2pの双方向通信
    • nng_pair0_open関数でソケットを作成する
  • SURVAYOR/RESPONDENT
    • survayor -> respondantの一方向通信
    • SURVAYOR: ブロードキャスト
      • nng_surveyor0_open関数でソケットを作成する
      • コンテキストサポート
        • 複数のリクエストを一斉配信
        • 最新の返信を採用
    • RESPONDENT: Finish or Discardを任意に選択可能
      • 時間切れでDiscard
      • nng_respondent0_open関数でソケットを作成する
  • BUS
    • ネットワークメッシュ間のメッセージ交換
    • P2P
    • nng_bus0_open関数でソケットを作成する
    • cookedモードはヘッダレス
    • rowモードはpipe idが渡される
ktz_aliasktz_alias

非同期IO

  • ノンブロッキング
  • 結果は登録したコールバックで受ける
    • nng_aio_alloc関数でコールバックを登録する
    • 第3引数(void *)に渡した値がコールバックに渡される
      • ソケットなどを渡す
  • リクエストキャンセルをサポートする
    • タイムアウトによるキャンセルもサポート

https://nng.nanomsg.org/man/v1.8.0/nng_aio.5.html

IOの待機

複数のソケットの待機を

while (true) {
    nng_aio_wait(...); // 一つ目のソケットの待機
    nng_aio_wait(...); // 二つ目のソケットの待機
}

とすると、一つ目のソケットの処理が完了するまで、二つ目のソケットの処理が行えなくなり、スループットが低下する。

回避策として、それぞれのソケット用に別スレッドを起動して、そこで待機させる。
無限ループのままだとスレッドを終わらせることができないため、キャンセルやソケットクローズかどうかの判定も行う。

while (true) {
    nng_aio_wait(...);

    switch (nng_ctx_result(...) {
        NNG_ECANCELED => {
            // キャンセルされた場合
            break;
        },
        NNG_ECLOSED => {
            // ソケットがクローズされた場合
            break;
        },
        else => {
            // 正常系
            // 受け取ったメッセージをハンドリングする
        }
    }
}
ktz_aliasktz_alias

受信の多重化について

  • 後方互換性のためnng_pollが残されてはいるが、非同期IO+コンテキストで組むことが基本。
  • 非同期IO+コンテキストの場合、単に複数のソケットを受信待ちにしておけばよく、やってきたものから順に処理してくれる。
    • ただし場合によってはREQ->REQになる可能性もあるため、自前での状態遷移の管理とキューイングが重要
ktz_aliasktz_alias

HELOメッセージ

サーバがnng_listenでバインド後、nng_ctx_recvを呼ぶことでやっと受信待ちにできる

  • しかしクライアントはnng_listenでバインドが完了していれば、nng_connectは成功する
  • nng_ctx_recv未完了で投げられたメッセージは捨てられる

クライアントはnng_connectを呼んだ後

  • サーバにHELOメッセージを送る
  • サーバはACKをレスポンスとして返す

のフローの成立をもって接続の確立とみなす必要がある。

nng_ctx_recv前にHELOを送ってしまうケースの対策として、REQソケットの送信にタイムアウトを設ける。
REQソケットはタイムアウト後に自動再送するため、サーバが生きていれば、いずれは接続が確立する