サーバーサイドSwiftを実運用してみた

  • このエントリーをはてなブックマークに追加

読了時間15分

こんにちは。リードアーキテクトのItoです。
前に予約していたNuAns NEOが届きました。かなりいい感じです。iPhoneと比べてしまうと、カメラ性能とアプリの少なさが気になりますが。

前回の記事では、Nodeベースのプロジェクト(Webサーバー)をSwiftに置き換えられるかという部分の話をしました。今回は前回からのアップデートや実際に運用してみたSwiftベースのサーバーサイドの話もしたいと思います。

Swift全体の動き、Swift本体(コンパイラ)のlatest build(masterブランチ)はSwift 3系になりました。
Swift(特にオープンソース版)の最新情報を追いたい場合は、以下のソースが参考になりました。

また、弊社サービス、カメリオSwiftテーマと同Swift lang updatesにも最新情報を追加していますので、見てみてください。

MySQLライブラリのアップデート

https://github.com/novi/mysql-swift
前回作成を始めたところのMySQLのライブラリですが、少しずつアップデートし、実運用をするようになりました。
RDBMSでSQLと静的な型の言語を相互にうまく使うのは大変ですが、上記ライブラリのコンセプトはなるべく動的なSQLやキャストを避け、データベースからの結果を早いうちにSwiftの世界に持っていくことです。
自分のアプリケーションで使う場合は、クエリ結果とModelの間が正しくデコードできるかをテストすればよくなるので、デバッグも楽になります。
ただし、ORマッパーではないので、それとは少しコンセプトが異なります。

また、新しくWebフレームワークライブラリKunugiを作成中で、こちらはまだ実運用をしていませんが、Express(Node.js)ベースのWebサーバーの置き換えしようとしているところです。
このライブラリの提供する機能の範囲は、Node.jsのExpress(どちらかというとKoa)に近い思想で作られています。
HTTPのサーバー機能自体を持ちませんのでNest準拠のサーバーやMiddlewareを自分で用意します。(後述)

興味のある方は試してみて、Twitterなどで質問やIssueやPull requestをいただけるとありがたいです。(日本語でも大丈夫です。)

SwiftでWebサーバーを作る場合のライブラリ間のインターフェース問題

Swiftでは、Nodeのようにhttp(s)の部分が標準ライブラリとしてあったり、Goのように
httpからurl、tcpなど数多くの標準ライブラリがあるわけではありません。
現状Swiftには、CocoaベースのFoundationだけしかありませんので、Webサーバーを作るには足りないものが色々あるでしょうし、Objective-C時代のFoundationの設計がいいとは言えないところもあります。
TCPServer, HTTPServer, SSL, URL, URLRequest, URLResponse, Cookie, Session, WebServer, Middleware, HTML Renderer…
Webサーバーを構成するうえで、必要な上のような多くのレイヤーが標準ライブラリに含まれていないため、それぞれの自分の仕様で作られたものが数多く出てきました。
(私のライブラリも同じく)
色々なライブラリが出てくるのはもちろん歓迎すべきことだと思いますが、
インターフェースの乱立は相互の利用ができなかったり、同じようで少し違うライブラリがたくさん出てくることになります。
最近公開された、IBM Swift Package Catalog
JSONパーサーだけでもたくさんあります。

そこで、Nestのようなプロジェクトは、乱立するHTTPServerとWeb Frameworkのリクエストとレスポンスの型を統一して、そのインターフェースに合うもの同士が相互に組み合わせられるようにしようとしているものです。
作成中のWebフレームワークはこのインターフェースに沿っています。

また、I/O非同期の問題もあります。Node.jsであれば必然的にIOが非同期になるため
ライブラリも非同期になってきます。
しかし、SwiftではI/Oをどうするかは各Webサーバーやライブラリ、データベースのドライバーによるため、同期IOのライブラリもある中、非同期IOのライブラリもあります。
現段階では同期IOのものが多いですが、今後も非同期IOのものも色々出てくるのではないでしょうか。
また、Cベースのライブラリは同期IOのものが多いです。

現時点で作られているやや大きめのフレームワークとWebサーバーは以下のものがあるようです。(全部は把握できていませんが)

Swiftベースのアプリケーションを実運用する

今回はAPIからXMLのデータを定期的に読み込んで、データベースに保存するというようなタスクを実行するデーモンを作成しました。
形としては、Twitter Search APIなどを定期的にクロールして、ツイートをデータベースに保存するようなものです。

使用したライブラリ

  • SchedulerKit
  • CrawlerKit

SchedulerKitとCrawlerKitは社内のライブラリになってしまうのですが、
SchedulerKitはタスクやジョブをCronのように実行できるライブラリ、
CrawlerKitは加えてHTTPなどのリクエストやXML, JSONパースの処理、ユーティリティを提供します。

現時点では、Foundationはまだ実装・テストされていない部分も多く、オープンソース版のSwiftとの相性があまり良くないため、Foundationを使う部分はNSDateやNSURL、NSXML…などのクラスにとどめ、他はZewoのライブラリを使用しました。
実際XcodeでビルドできるSwiftコードをLinuxに持っていってもコンパイルできない、または未実装なことがよくあります。

NSURLSessionはまだLinuxで使えないため、HTTPクライアントとして、C言語のlibcurlをベースとしたSeeURLを使いました。
CのAPI(ヘッダーファイル)があるライブラリは、そのままSwiftから扱えるので、既存の安定したCライブラリがある場合にはSwiftで再実装しなくてもよいので大きな利点になると思います。libxml2なんかも使えると思います。

上記ライブラリを使用したクローラーで、1時間あたり30000件のデータ(Twitterでいうとツイートにあたる)をAPIから取得したところ、
OS X上では2,3日連続稼働できました。(プログラムのアップデートが多いためそれ以上の実績はまだです。)
基本的にDB処理かAPIリクエストが常に動きっぱなしです。
メモリ使用量(プライベート+共有)は60MBくらいで安定しました。
消費量もNodeなどに比べても多くない気がします。
とはいえガーベジコレクションが無いため一旦リークすると解消するのが面倒な気もします。GCの是非はまだ分からない状況です。
今のところI/Oに関しては、使用ライブラリがすべて同期IOのスレッドモデルになっているので、スレッドごとにIOをブロックしてしまうモデルです。
しかしながら、APIをクロールするだけの機能であるため、そこまで非同期IOの性能を必要としません。それもあり、先にWebサーバーよりもクローラーやデーモンでのSwift化の検証をしています。
ゆえにCPUはパースのみでフルに使われ、それ以外のIO待ちではほぼ0になります。

もちろん、それでいて、Swiftの利点try catchやOptional, Enum, Protocolなどが使えるのでかなり快適です。使えるところではSwiftらしい機能は積極的に使っています。
ユニットテストと併せるとリファクタリングも気軽にどんどんできるので、より良いコードや設計を目指すにはとてもよいです。

オープンソース版Swiftでの開発環境

最終的に主にLinuxなどで動かす場合でも、開発やデバッグにはXcodeを使いたいところです。(エディタ+強いプラグインでもかまいませんが)

フレームワークの場合

フレームワークの場合は、Xcodeで通常通りCocoa Frameworkとしてプロジェクトを作成すれば問題ありません。
また、オープンソース版ではPackage.swiftが必要になりますが、これも作成します。

アプリケーションの場合

残念ながらXcode標準のテンプレートのCommand Line Toolを使おうとしてもうまくいきません。アプリケーションからフレームワークを呼び出す場合などは、通常のCocoa Applicationのようなバンドル形式のappにする必要があるようです。
Cocoa ApplicationからStoryboard部分を消してもいいのですが、Zewoが公開している、
Swift Command Line Application Templateが便利です。
こちらもオープンソース版ではPackage.swiftにアプリケーションの名前と依存するライブラリを書いておきます。

フレームワークとアプリケーション共通して、.swiftのソースコードはSourcesフォルダに入れる必要があるため、
プロジェクトの構造を以下のようにオープンソース版に合わせるほうがいいです。

our-great-swift-lib
├Package.swift
├Sources
│├main.swift (if it is an application)
│└Model.swift
├Tests
│└ModelTests.swift
└Xcode
└OurGreatLibrary.xcodeproj

XcodeのプロジェクトにはSourcesやTestsを追加し直せばそこを参照してくれるはずです。
フォルダ構成は実際のライブラリ、mysql-swiftなどを参照してみてください。

また、今後はzewo-devxcakeをベースとしたPackage.swiftからXcodeのプロジェクトを生成するツールも出てくるでしょう。

テスト

ユニットテストはSwift 2やXcode 7世代になってかなり楽に書けるようになってきました。
Xcodeプロジェクトを使う場合は通常通り、ユニットテスト(Product->Test)が使えますが、オープンソース版では少々面倒です。
FoundationやCベースのライブラリなどを使うと、プラットフォームごとに挙動が異なる場合があるため、Linuxなどでもテストを実行しておきたいところです。
swift-corelibs-xctestにあるように、allTestsを定義し、XCTMainを使って自分でテストクラスを指定する必要があります。テスト関連のswiftとmain.swiftは手動でビルドします。(swift buildを使う方法もあるようですが、今回は試していません。)
ビルド方法はMakefileを見てみてください。

このシリーズの次回のトピックは機械学習とSwiftを予定しています。(それか今度こそWebサーバーについて)

そして来週はいよいよ try! Swift ですね。

最先端情報吸収研究所 – AIAL

際限ない情報の中から、自分に価値のある情報を効果的に吸収することは、かつてなく大きなチャレンジです。最先端情報研究所はニュースアプリ「カメリオ」、レコメンドエンジン「カメクト」を提供する白ヤギコーポレーションのR&D部門として、データサイエンスの力でこの問題を解決していきます。白ヤギでは現在研究開発メンバーを募集しております。ご興味のある方は是非下記サイトを御覧ください!

Date:2016-02-24 Posted in:バックエンドの技術 Text by: