mike-neckのブログ

Java or Groovy or Swift or Golang

reactor-netty コールドリーディング(3)

HttpRequest の組み立て

  1. HttpClient#get などのメソッドから HttpClient#request メソッドを呼び出し
  2. HttpClient#request にて Mono<HttpClientResponse> を返すが、実体は MonoHttpClientResponse
  3. 2.の際に HttpClient#handler というメソッドが呼び出され、 Function<HttpClientRequest,Publisher<Void>>MonoHttpClientResponse に渡される
  4. 3.はユーザーのハンドリングにて Mono<SomeRequestBaseModel> が生成されることを予期している
  5. MonoHttpClientResponse#subscribe が呼び出されると、 Mono#defer にて Mono<NettyContext> が生成されて、 HttpClientResponse にキャストされたものが subscribe される
  6. 5.の defer の中では HttpClient#newHandler が呼び出される
  7. 6.の HttpClient#newHandlerMono<NettyContext> を返すメソッド
  8. 6.の HttpClient#newHandler に渡す HttpClientHandlerBiFunction<NettyInbound,NettyOutbound,Publisher<Void>> を実装するクラスで、ここの apply メソッドにて NettyInbound (= HttpClientOperations)をもとに リクエストを組み立てる
  9. HttpClient#newHandler は実際には TcpBridgeClient#newHandler に委譲していて、更に TcpClient#newHandler に委譲している
  10. TcpClient#newHandler では Bootstrap の取得が行われる
  11. TcpClient#newHandler の最中に呼び出される TcpClient#doHandler メソッドの戻り値 ContextHandler<SocketChannel>ChannelInitializer<SocketChannel> を継承したクラス
  12. ContextHandler<SocketChannel> は抽象クラスで実装クラスは PooledClientContextHandler(ChannelPool が利用可能な場合) または ClientContextHandler
  13. ContextHandlerinitChanneldoPipeline(Channel) を読んで各サブクラスで ハンドラーを登録したあとに ChannelOperationsHandler を登録する

reactor-netty コールドリーディング(2)

Bootstrap の組み立て

Bootstrap は以下の2箇所のいずれかで組み立てる

  • PoolResources#selectOrCreate(SocketAddress,Supplier<? extends Bootstrap>,Consumer<? super Channel>,EventLoopGroup)
    • PoolResources が利用できる場合のみ
    • PoolResources では接続先アドレス(ポート含む)ごとに Channel をプールしている
  • TcpClient#newHandler(BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>>, InetSocketAddress, boolean, Consumer<? super Channel>)

reactor-netty コードリーディング(1)

  1. HttpClient#create(String,int) : アドレスとポートを Consumer<HttpClientOptions.Builder> にラップして(options.host(address).port(port)) HttpClient.Builder#options(Consumer<HttpClientOptions.Builder>) で設定して HttpClient を生成する
  2. HttpClientOptions.BuilderClientOptions.Builder<B extends ClientOptions.Builder<B>> を継承したクラス
  3. HttpClientOptions.Builder に追加されているオプションは acceptGzip
  4. ClientOptions.Builder<B extends ClientOptions.Builder<B>>NettyOptionsBuilder<Bootstrap, ClientOptions, B> を継承したクラス
  5. ClientOptions.Builder にて設定可能なオプションは pool の使用有無(デフォルトは使う)/インターネットプロトコルファミリー(inet4/inet6)/ホスト/ポート/proxyのオプション
  6. ClientOptions.Builder にて Bootstrap のデフォルトオプションを設定している. 設定値は下記の表を参照.
  7. ClientProxyOption はproxy の設定(username/password/アドレス/proxyを通さないホストアドレス/proxyのタイプ)を持つ
  8. proxy のタイプによって異なる ProxyHandler を生成する(ProxyHandlerChannelDuplexHandler のサブタイプ)

ClientOptions.Builder にて設定しているデフォルト Bootstrap のオプション

オプション
ChannelOption.CONNECT_TIMEOUT_MILLIS 30000
ChannelOption.AUTO_READ false
ChannelOption.SO_RCVBUF 1024 * 1024
ChannelOption.SO_SNDBUF 1024 * 1024

Swift で JSON のデコード

f:id:mike_neck:20180609045321p:plain

Swift4 での JSON 文字列からオブジェクトへの変換方法のメモ. もっと詳しく調べたい場合は公式のドキュメントを調べた方がよい.


これからデコードするJSON

{
  "id": 219038
, "name": "James Thunder"
, "password": "s0r23ndsn0q3mf083259"
, "created_at": "2015-04-13T14:20:32"
, "email": [
  {
    "value": "jim@example.com"
  , "primary": true
  }
]
}

JSON にあわせたデータ. protocol Codable に準じた struct で作る

struct User: Codable {
    let id: Int64
    let name: String
    let password: String
    let email: [Email]
    let created_at: String
}

struct Email: Codable {
    let value: String
    let primary: Bool?
}

デコードするコード

let decoder = JSONDecoder()
let user: User = try! decoder.decode(
    User.self,
    from: userJson.data(using: .utf8)!
)

json がスネークケースだけど、オブジェクトのプロパティはキャメルケースにしたい場合は、ネスト enum を作って、 CodingKeys protocol と String を継承させたマッピングを作る.

struct User: Codable {
  let id: Int64
  let name: String
  let password: String
  let email: [Email]
  let createdAt: String

  enum UserCodingKeys: String, CodingKey {
    case createdAt = "created_at"

    case id = "id"
    case name = "name"
    case password = "password"
    case email = "email"
  }
}

なお JSONDecoder の呼び出し方は同じ.


場合によっては配列で返されることもある.

let usersJson = "[\(user)]"

この場合は、 decoder.decode<T>(_ type: T.Type, from data: Data) の呼び出し時の type:[User].self を指定する

let users: [User] = try!
    decoder.decode(
        [User].self,
        from: usersJson.data(using: .utf8)!
)