mike-neckのブログ

Java or Groovy or Swift or Golang

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)!
)

Groovy製のプロビジョニングツール infrastructor

f:id:mike_neck:20150917235151p:plain

infrastructor は Groovy でできたサーバープロビジョニングツールで、まあ、 Chef とか itamae とか Ansible の類のツールです。2018/06/07 現在のバージョンは 0.1.8 ということで、まだ開発が始まってから1年くらいの若いツールです。

github.com

この記事では infrastructor のチュートリアルをやってみます。


インストール

インストールは sdkman を使います。sdk は古いバージョンのものだと、新しい candidate の名前を判断できないので、最新版(2018/06/07 時点で 5.6.4+305)を使ってください。なお、 Java 9 以降にしているといろいろと警告がうるさいので(groovyさん…) Java 8 にしておきます。

$ sdk i infrastructor

Downloading: infrastructor 0.1.8

In progress...

######################################################################## 100.0%

Installing: infrastructor 0.1.8
Done installing!


Setting infrastructor 0.1.8 as default.

完了後、次のとおりにコマンドをうって、バージョンを確認できればインストール成功です。なお、 Java 9 以降を使うと groovy のリフレクションに関する警告がうるさいので、 Java 8 を使っています。

$ infrastructor version
version:    0.1.8
revision:   89b025d4bc710e24859d096bce0de7f2d48dcbc8
build date: 22-05-2018 13:44

チュートリアル

チュートリアルでは Docker を使います。 Docker のインストールは別途 Docker のサイトを参照してください。

infrastructor からチュートリアル用のイメージが配布されています。それをローカルに持ってきます。

$ docker pull infrastructor/ubuntu-sshd:latest

なお、このイメージの Dockerfile はこちらを参照。

github.com

で、このイメージを起動しておきます。

$ docker run -d -p 10022:22 --name infra-test infrastructor/ubuntu-sshd:latest

で、次のファイルを example.groovy という名前で作ります。

inlineInventory {
  node host: 'localhost', port: 10022, username: 'devops', password: 'devops'
}.provision {
  task name: 'install some packages', actions: {
    shell 'sudo apt update'
    ['tmux', 'mc', 'htop'].each { pkg ->
        shell "sudo apt install $pkg -y"
    }
  }
}

次のコマンドを実行します。

$ infrastructor run -f hello.groovy

実行に時間がかかりますが、次のように表示されます。

$ infrastructor run -f hello.groovy 
[INFO] running script: hello.groovy
[INFO] task: 'install some packages'
[INFO] task: 'install some packages', done on 1 node|s
[INFO] running script: hello.groovy - done

EXECUTION COMPLETE in 1 minutes, 2.437 seconds

テスト機能はないようなので、とりあえず、 Docker のインスタンスにログインして確認します。

$ docker exec -it infra-test bash
root@53aa2afc480b:/# which mc
/usr/bin/mc
root@53aa2afc480b:/# which tmux
/usr/bin/tmux
root@53aa2afc480b:/# which htop
/usr/bin/htop
root@53aa2afc480b:/# exit
exit

インストールできているようです


前から JVM で動くプロビジョニングツールがあったら本気出すと思っていたのですが、本気を出す時が来たようです。

Swift-NIO の ChannelInboundHandler/ChannelOutboundHandler のメモ

単なるメモ

f:id:mike_neck:20180529230134p:plain

Swift-NIOChannelInboundHandler/ChannelOutboundHandler を記述する際のポイント

ChannelInboundHandler

  • Netty の ChannelInboundHandler と同じく、基本的には channelRead(ctx:ChannelHandlerContext, data: NIOAny) を実装する
  • NIOAny 型は Netty での Object とに該当するが、それらの型は associatedtypeInboundIn に型変換をおこなう
    • 変換は unwrapInboundIn(_: NIOAny) -> InboundIn にておこなう
  • associatedtype で関連される型は次のものがある
    • InboundIn : channelRead で渡される型
    • InboundOut : 次の ChannelInboundHandler に渡す型
    • OutboundOut : ChannelHandlerContext に書き込む型(書き込む場合に必要)
  • ChannelHandlerContext に値を渡す場合は次のように行う
    • fireChannelRead に値を渡す場合は NIOAny を渡す. その際は wrapInboundOut(_: InboundOut) -> NIOAny にて変換する
    • write(_: NIOAny, promise: EventLoopPromise<Void>?) に値を渡す場合は NIOAny で渡す. その際 wrapOutboundOut(_: OutboundOut) -> NIOAny にて変換する.

ChannelOutboundHandler

  • Netty の ChannelOutboundHandler と同じく write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) を実装する
  • data: NIOAnyassociatedtype OutboundIn にて型を指定することで型変換をおこなう
    • unwrapOutboundIn(_: NIOAny) -> OutboundIn で変換する

class EchoInboundHandler: ChannelInboundHandler {
  typealias InboundIn = ByteBuffer
  typealias OutboundOut = String

  func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
    var buffer = unwrapInboundIn(data)
    let message = buffer.readString(length: buffer.readableBytes)
    print("message: \(message)")
    ctx.write(wrapOutboundOut(message), promise: nil)
  }
}

class EchoOutboundHandler: ChannelOutboundHandler {
  typealias OutboundIn = String
  typealias OutboundOut = ByteBuffer

  func write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
    let message = unwrapOutboundIn(data)
    var buffer = ByteBufferAllocator().buffer(message.utf8.count)
    buffer.write(string: message)
    ctx.writeAndFlush(wrapOutboundOut(buffer), proise: nil).whenComplete({ ctx.close() })
  }
}

これを書き終えてから、公式のドキュメントがあることに気がついた…(´・ω・`)

NIO Reference