Event driven networking for Swift | try! Swift Tokyo 2018 Day1-7

Swift が iPhone, iPad, Mac, Apple Watch そして Apple TV のアプリを開発する言語として選ばれていることはみなさんご存知でしょう。この数年、Swift は Linux で動作するサーバーサイドアプリケーションを開発するのにも優れた言語としても浮上してきました。現在、Apple, Facebook, Google, Netflix, Twitter などの企業が運用する大規模な環境では、Java、C ++などの言語による、パフォーマンスに最適化されたネットワークフレームワークが使われています。このトークでは、Apple のサーバーインフラストラクチャーチームが長年の経験を活かし、どのように高性能でスケーラビリティの高いネットワークアプリケーションを作成して、どのように開発時にSwiftを利用しているかについて説明します。

Event driven networking for Swift

f:id:niwatako:20180301143921j:plain

Event driven networkingについて、バズワードでもあるので説明したいと思います。

非同期が何故大事なのか

f:id:niwatako:20180301144002j:plain

答えは簡単、リソースを最大限使いたいと思うだろうからです

f:id:niwatako:20180301144012j:plain

サーバーとモバイルの間での読み書きはおもったよりも低い頻度で起きていて、常にIO待ちとなってしまう。

f:id:niwatako:20180301144117j:plain

非同期でないネットワーキングプログラムではスレッド、コネクションを一つ作成するか、コネクションごとにスレッドを用意するということになると思います。

IOオペレーションがスレッドの一つをブロックするとたちまち困ってしまいます。

こちらなら一つのスレッドが複数のコネクションを管理できます。

f:id:niwatako:20180301144212j:plain

これまでIOに持っていた前提条件が壊れることになります。

データの中で怒ってからデータが返ってくるという前提は壊れます。

ネットワークサービス全体が大きく複雑化してしまいます。

この複雑さを許容しようとする理由を理解するには、常にトレードオフがあるが、ハイスケールサービス(システム)を口にする時、数百から数千を同時に処理しなければならなくなるということがあるのを理解する必要があります。

f:id:niwatako:20180301144340j:plain

スレッドを使うとコストはある。

しかし、メモリオーバーヘッドしたとしても、他にもパローマンスコストになることがある。OSが一つのスレッドで進展する時にOSのスケジューラーに処理されるが、インパクトが一番大きいのは、必ずそのスイッチのコンタクトが必要だということです。

恐らく最悪、になるのは、CPUコアのキャッシュの廃棄です。

オペレーションは高速だったが、このトラッシングによりスローに鳴ってしまう。

キャッシュがないからデータが底にアクセス出来ないということになる。

こうした複雑性をどう解決していくか。Non Blocking IOを使っていきましょう

f:id:niwatako:20180301144514j:plain

JVMや他の言語では、ネッティー(?)の開発に投資をしている

f:id:niwatako:20180301144546j:plain

ここで、もしかしたら新しいものを作ったほうがいいかもという話になりました。

SwiftNIOと言うものです。

Swift用のNettyだと思って下さい

これまで成功してきたとおり、柔軟なのに、これまでは複雑でした。AppleはNettyで多くの経験を積み、複数の人間がNettyプロジェクトに関わっています。

Nettyは大きな人気の高いプロジェクトApacheやNoSQLデータベース、Sparkなどが使っています。GRPCも使っています。実証済みのAPIになりということです。

本当に最後に申し上げたいのはNettyは既に経験がある人にとって容易になるということです。Swiftの探索がとても簡単になります。JVMユースケースと似ているわけですから。

低レベルネットワーキングフレームワークです

f:id:niwatako:20180301144810j:plain

TCPプロキシなども使えますし、DNSサーバーなどもそうです。

高レベルフレームワークではありません。KituraやVaporではありません

f:id:niwatako:20180301144830j:plain

そのひとつ下のレイヤーです。

殆どのユーザーは直接使わないでしょう。しかしその上に構築されたフレームワークを使うことになります。

簡単に説明していきます

f:id:niwatako:20180301144927j:plain

イベントループグループがあります

f:id:niwatako:20180301144939j:plain

スレッドプールと考えてもいいでしょう。

一つのイベントループは一つが専用のスレッドを持つ。これが大事な理由は後で話す。

チャネルそのものがエンドポイントを抽象化したものです。それが何を意味するかは実装に関わります。

ソケットのように考えて良いと思います。TCPUDPなどです。

そしてチャネルパイプラインがあります。チャネルにアサインされます。チャネルパイプラインは複数のチャネルハンドラを持ちます。ハンドラそのものはロジックの処理をさせてくれます。インバウンド、アウトバウンドデータをインターセプトできます。

これも後で話します。

ブートストラップとかもあります。チャネルをセットアップします。

コンテナはデータストラクチャをコピーします。高度なオペレーションができます。

重要なのはSwiftがCopy on writeの機能があるので関数から関数に渡すのも簡単にできます。

イベントループの図式です

f:id:niwatako:20180301145202j:plain

イベントループと読んでいますが無限ループの中で実行されています。シャットダウンもあるのですが。

様々なイベントタイプを収集処理していきますlイベントループは複数の処理を扱うことができます。一つのスレッドでコネクションを多重化する必要があるからです。

すべてのチャネルからコネクションをアクセプトします

これが終わるとタスクのキューに入ります。それを実行します。それを継続できるまでブロックします。これがここの基本的な考え方です。

興味深いとkロオへ行きましょう

これがチャネルパイプラインです。基本的に、複数扱うことができます

f:id:niwatako:20180301145400j:plain

モバイルデバイスで十分読み込めない時はとめてメモリ稼働率を高める。BackPressureHandlerで。

この3つの構成要素があって、完全なechoサーバーができます。暗号化もされています。あんごうかが不要ならOpenSSLハンドラをれムーブすれば良いです。

f:id:niwatako:20180301145519j:plain

チャネルハンドラは2つのコールバックがあります。パイプラインにAddされたのとRemoveされた時。

インバウンドハンドラ

f:id:niwatako:20180301145539j:plain

メソッドが幾つかあります。チャネルがアクティブになった時、Inactiveは接続がKillされた時。ChanelReadは読み出される時、ReadCompleteは終わった時。ErrorgaおきたCaught

Outboundは逆

f:id:niwatako:20180301145640j:plain

Promissというのはオペレーションが終わると呼び出されるもの

例です

f:id:niwatako:20180301145716j:plain

flushというのはSysCall内のデータをすべて捨てる。 closeで閉じる、もうリカバーできません

どう組み合わせていくのか

f:id:niwatako:20180301145754j:plain

echo サーバーをマウントしていく

パイプラインをOnTheFlyでできる

プロトコルのアップデートなどをハンドラーのリムーブをしながらできる

SwiftNIOがエコシステム全体にどのような影響があるのか

f:id:niwatako:20180301145849j:plain

Nettyと同じようなものかと思います

すでにSwiftコンパイラチームに最適化するように言っています。

今日オープンソース化されました

f:id:niwatako:20180301145955j:plain

github.com

Appleから7人ぐらい来ているので、恥ずかしがらずに話して下さい!

[広告]面白かったら、ためになったら

  • はてなブックマークSwift タグをつけてブックマーク!
  • 「インターネットで生活を楽しく豊かにしたい」仲間を募集しています
  • Bitcoin: 3KGqXtR1ZaGVdkvcw8CCNrkDxDhdbZBYHL