この記事の要点
- Swift サーバーエコシステム向けの新しいオープンソースプロジェクト Swift Service Discovery(swift-service-discovery)が発表されました。サービスディスカバリ(service discovery)の標準 API を定めた Swift パッケージで、特定のバックエンドに縛られずにサービスインスタンスの位置情報を問い合わせられるようにします。
- swift-log や swift-metrics と同様に、このパッケージは API のみを定義します。実際の機能は DNS ベースや key-value ストアベースといったバックエンド実装が提供します。
- API には、現在のインスタンス一覧を一度だけ取得する
lookupと、現在の一覧を受け取ったうえで以降の変更も購読し続けるsubscribeの 2 つが用意されています。
何が発表されたのか
Swift Service Discovery は、サービスディスカバリのための標準 API を確立することを目的とした Swift パッケージです。サービスディスカバリの仕組みには DNS ベースや key-value ストアベースなどさまざまな方式がありますが、このパッケージはそれらに共通する API を 1 つに定めることで、バックエンドを差し替えても利用側のコードを変えずに済むようにします。
swift-log や swift-metrics と同じく、Swift Service Discovery が定義するのは API だけです。実際のサービスディスカバリ機能は、この API に適合したバックエンド実装が提供します。
API の中心となるのは次の 2 つの概念です。
- Service Identity(サービスの識別子): 各サービスは一意な識別子を持ちます。バックエンド実装が使う識別子の型を
Serviceで表します。 - Service Instance(サービスインスタンス): 1 つのサービスは 0 個以上のインスタンスを持ち、それぞれにロケーション(典型的にはホストとポート)が結び付きます。バックエンド実装が使うインスタンスの型を
Instanceで表します。
何に使えるのか
サーバーアプリケーションでの利用
問い合わせを行うアプリケーション側は、まず使いたいサービスディスカバリのバックエンド実装を依存に追加し、プログラムの最初でインスタンス化します。たとえば架空の DNSBasedServiceDiscovery をバックエンドとして選んだ場合は次のようになります。
// 1) サービスディスカバリのバックエンドパッケージをインポートする
import DNSBasedServiceDiscovery
// 2) 具体的な ServiceDiscovery オブジェクトを作成する
let serviceDiscovery = DNSBasedServiceDiscovery()
現在のインスタンス一覧を一度だけ取得するには lookup を使います(result は Result<[Instance], Error> です)。
serviceDiscovery.lookup(service) { result in
...
}
現在のインスタンス一覧を取得したうえで、以降の変更も購読し続けたい場合は subscribe を使います。
let cancellationToken = serviceDiscovery.subscribe(
to: service,
onNext: { result in
// このクロージャは最初に一度呼ばれ、
// 以降は変更が起きるたびに呼ばれる
...
},
onComplete: { reason in
// このクロージャは購読が終了したときに呼ばれる
...
}
)
...
// subscribe のリクエストをキャンセルする
cancellationToken.cancel()
subscribe は CancellationToken を返し、これを使って後から購読をキャンセルできます。onComplete は購読が終了したとき(たとえばサービスディスカバリのインスタンスが終了したときや、CancellationToken でキャンセルされたとき)に呼ばれるクロージャで、終了の理由は CompletionReason で判別できます。
バックエンド実装の作り方
サービスディスカバリのバックエンド実装は、ServiceDiscovery プロトコルに適合する必要があります。このプロトコルは lookup と subscribe の 2 つのメソッドを持ちます。
func lookup(
_ service: Service,
deadline: DispatchTime?,
callback: @escaping (Result<[Instance], Error>) -> Void
)
lookup は指定されたサービスの現在のインスタンス一覧を取得し、callback に渡します。サービスが未知の場合(たとえば登録が必要なのにまだ登録されていない場合)には、LookupError.unknownService の失敗を返すべきとされています。また、操作がいつ完了するかについては、deadline か既定のタイムアウトによって期限を設けるべきとされています。
func subscribe(
to service: Service,
onNext nextResultHandler: @escaping (Result<[Instance], Error>) -> Void,
onComplete completionHandler: @escaping (CompletionReason) -> Void
) -> CancellationToken
subscribe はサービスインスタンスを nextResultHandler に「プッシュ」します。
subscribeが最初に呼ばれたとき、呼び出し側は対象サービスの現在のインスタンス一覧を受け取ります。これは実質的にlookupの結果と同じです。- 以降は対象サービスのインスタンス一覧が変化するたびに通知されます。サービスレコードをいつどのように更新するかはバックエンド実装が完全に制御しますが、一覧が直前の結果と異なるものになったときには
nextResultHandlerに通知しなければなりません。
subscribe のリクエストごとに新しい CancellationToken が作られます。CancellationToken の isCancelled が true になっていたら購読はキャンセルされているので、バックエンド実装は対応する nextResultHandler の呼び出しをやめるべきです。
さらにバックエンド実装は、何らかの理由で購読が終了したとき(サービスディスカバリのインスタンスが終了する場合や、CancellationToken でキャンセルされた場合など)には completionHandler で必ず通知しなければなりません。これにより、購読側は必要に応じて改めて subscribe のリクエストを送れます。
導入・今後の位置づけ
発表時点でこのプロジェクトは始まったばかりで、コミュニティ主導で貢献を積極的に募っているとされています。Swift Service Discovery 本体への貢献に加えて、サービスの登録や位置情報を管理して問い合わせ可能にする、互換性のあるバックエンド実装も求められています。
Swift Service Discovery はオープンソースプロジェクトであり、ソースコードは GitHub で公開されています。フィードバックや議論は Swift フォーラムの server カテゴリで、バグ報告は GitHub の issue トラッカーで行えます。