Amazon SNS のモバイルトークン管理についてのベストプラクティス

Amazon SNS
131件のシェア(ちょっぴり話題の記事)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

皆様、Amazon SNS は活用されていますでしょうか?弊社でもモバイルアプリの開発案件において、プッシュ通知機能は要件として挙げられることが多く、実際に数多くのアプリで SNS を用いたプッシュ通知機能を開発してきました。もはやモバイルアプリに必須の機能!と言えるでしょう。

SNS のメリットの1つは各プラットフォーム向けに提供されているモバイルトークンの管理を代わりに行ってくれるという点です。各プラットフォーム向けに自力で管理すると大変面倒な管理が、SNS を使うだけで非常に楽に管理することができます。

そのまま SNS に頼るだけでも大変便利なのですが、最新の登録情報を維持し続けるために行っておいたほうが良い、推奨されるアプローチがあります。

このブログでは SNS のモバイルトークンの管理のノウハウについてまとめられているこちらの記事をベースに、モバイルトークン管理のベストプラクティスを意訳しつつまとめたいと思います。

アプリケーション、エンドポイント、トークン

まず、SNS の動作の仕組みからおさらいしていきましょう。SNS からモバイルデバイスにプッシュ通知を送るには、2つの情報が必要です。

  • アプリケーションとモバイルデバイスの組み合わせで作られたトークン
  • アプリケーションからプッシュ通知を送るための資格情報

アプリケーションとモバイルデバイスの組み合わせで作られたトークン

トークンは、モバイルデバイスの OS によってアプリケーションのために発行された文字列です。これは、一意に特定のモバイルデバイス上で実行しているモバイルアプリのインスタンスを識別し、このアプリ-デバイスのペアの一意の識別子として考えることができます。そのため、1つのモバイルデバイスでもアプリ毎にトークンは異なります。

アプリケーションからプッシュ通知を送るための資格情報

SNS からプラットフォームに接続し、プッシュ通知を送るためには、そのプラットフォームに接続するための資格情報が必要です(形式は各プラットフォームで異なります)。1組の資格情報がアプリケーションごとに発行され、そのアプリケーションのすべてのモバイルデバイスにメッセージを送信するために使用することができます。

SNS での扱い

SNS では、資格情報を PlatformApplication オブジェクトとして、トークンを PlatformEndpoints オブジェクトとして表現します。各 PlatformEndpoint は、1つの特定の PlatformApplication に属しており、すべての PlatformEndpoint は対応する PlatformApplication に格納されている資格情報を使用して通信することができます。

SNS へのアプリケーションのトークンの登録

CreatePlatformEndpoint の振る舞い

アプリケーションのトークンを SNS に登録するには、CreatePlatformEndpoint という API メソッドをコールします。このメソッドはパラメータに PlatformApplication の ARN と各プラットフォーム向けのトークンを取り、レスポンスに生成した PlatformEndpoint の ARN を返します。

CreatePlatformEndpoint は冪等性 *1であり、以下を実行します。

  • PlatformEndpoint が既に存在する場合は新たに作成せず、その PlatformEndpoint の ARN を返す
  • 同じトークンの PlatformEndpoint が異なる属性で既に存在する場合は新たに作成せず、何も返さず例外をスローする
  • PlatformEndpoint が既に存在しない場合、PlatformEndpoint を新たに作成し、その ARN を返す

この動作は CreatePlatformEndpoint を呼び出すことが常に安全であるということを保証しています。何回コールしたとしても、1つのトークンに対し、PlatformEndpoint が必ず1つしか存在しない状態を維持することができます。そのため、アプリ起動時に呼び出すように設計しても問題ありません。

PlatformEndpoint を登録するときにチェックする必要がある項目

上記の振る舞いでは、アプリケーションを再インストールしたときや対象の PlatformEndpoint が Disabled になっていたときには使えません。登録時の処理で、次の点をチェックすると良いでしょう。

  • PlatformEndpoint がアプリ-デバイスの組み合わせで存在することを確認する
  • PlatformEndpoint が内包しているトークンが、最新の有効のトークンであることを確認する
  • PlatformEndpoint が Enabled で、使える状態にあるか確認する

PlatformEndpoint を安全に最新の状態を維持するアプローチ

次の流れは、多様な状況下の中で、動作する最新の有効な PlatformEndpoint を返すようなフローです。このアプローチは、初めに登録が実行されたかどうか、PlatformEndpoint が既に存在するかしないか、Enabled か Disabled か、正しいトークンを持っているかどうかに関わらず動作します。このアプローチは冪等性であり、何回呼び出されたとしても安全です。重複した PlatformEndpoint を作成することはありませんし、PlatformEndpoint が Enabled かつ内包しているトークンが既に最新の場合は変更を加えません。

sns-flowchart

このアプローチは、新規登録時や再登録時など、どのタイミングでも採用することができます。PlatformEndpoint のトークンが変化したときのイベントを SNS で通知するときなどにも使うことができます(この場合は、最新のトークンの値で呼び出すことができます)。

なお、このアプローチは次のような注意点があります。

  • CreatePlatformEndpoint が呼び出されるケースは2つあります。1つは初回登録時のようにアプリケーションが PlatformEndpoint の ARN を知らない場合、もう1つは GetEndpointAttributes 呼び出し時に NotFound 例外がスローされたときです。GetEndpointAttributes を呼び出した時の NotFound 例外は、アプリケーションが PlatformEndpoint の ARN を知っているが、既に削除されている場合です。
  • GetEndpointAttributes は、PlatformEndpoint が作成されたばかりの場合でも、状態を確認するために呼び出されます。GetEndpointAttributes は PlatformEndpoint が既に存在するが Disabled になっている場合に呼び出されます。このとき、CreatePlatformEndpoint は PlatformEndpoint が既に存在する場合、成功しますが Enabled にしません。そのため、結果を返す前に PlatformEndpoint が Enabled になっているかどうかチェックする必要があります。

サンプルコードはこちらに書いてありますので、ご参照ください。

潜在的な落とし穴

モバイルトークン管理において、いくつかの落とし穴があります。留意して実装するようにしましょう。

古いトークンで CreatePlatformEndpoint を呼び出す

APNs (iOS) の場合、モバイルトークンは変わりませんが、特に GCM (Android) の場合は、トークンがコロコロよく変わります。そのため、アプリケーションが起動するたびに、アプリケーションが発行したトークンに都度変更していきたいところです。しかし、この解決策はいくつか注意点があります。

  • SNS は有効期限が切れたトークンを更新するために、GCM からのフィードバックを利用しています。いっぽう GCM は、無期限ではなく一定期間だけ古いトークンに関する情報を保持します。そのため GCM が古いトークンと新しいトークンとの関連性ついて忘れたら、SNS は PlatformEndpoint に内包されたトークンを更新できなくなります。そのとき、SNS は PlatformEndpoint を無効にします。
  • PlatformApplication は、同じトークンに対応する複数の PlatformEndpoint が含まれます。
  • SNS は、同じトークンで始まる PlatformEndpoint の数に制限(3つまで)を設けています。上限以上の新たな PlatformEndpoint を作成しようとした場合、InvalidParameter 例外が "This endpoint is already registered with a different token" というエラーメッセージと共にスローされます。

まとめると、アプリ起動時に都度 CreatePlatformEndpoint を呼ぶと問題が発生することがあり得る、ということになります。上述している登録フローのように、最新のトークンを SetEndpointAttributes で更新していくと良さそうです。

無効なトークンに関連付けられた PlatformEndpoint を再有効化する

SNS が APNs や GCM などのようなモバイルプラットフォームに向けた Publish を実行したとき、トークンが無効であったことが通知されると、そのトークンを内包している PlatformEndpoint を Disabled にします。以後、SNS は Disabled の PlatformEndpoint に対する Publish を拒否し、実行しません。

つまり Disabled になっている場合はもはや対象の PlatformEndpoint に対して配信できないことが明確になっているということです。再度 Enabled にした場合、SNS は Publish を実行しますが、メリットはありません。送信できなかった場合、また即座に Disabled になります。

Disabled になっている PlatformEndpoint を再び Enabled にする必要性は、アプリが再インストールされたときなどに発生します。こういったときには SetEndpointAttributes を呼び出し、有効なトークンをセットして Enabled に設定する必要があります。以後、最新のトークンがセットされた有効な PlatformEndpoint として扱われるので、Publish は成功します。

まとめ

モバイルトークンの管理は、特に登録時のフローに注意して実装しておく必要があることが分かりました。SNS は、最も速く、最も柔軟性があり、最も信頼性が高く、最も費用対効果が高いプッシュ配信サービスですので、このベストプラクティスを取り入れ、素敵なプッシュ配信ライフを送りましょう!

なお、AWS から公式サンプルが公開されていますので、こちらもご覧いただけるとより理解が深まると思います。

http://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html

参考

脚注

  1. 端的に言うと、何回行っても同じ結果が返ってくることです。英語では Idempotent です。
  • Shinichi Aoki

    ひとりで複数のデバイスを登録する場合、このフローだと対応できないように思いましたがどうでしょうか?

    具体的には、
    最後で「トークンが異なる」場合どれを対象にSetEndpointAttributesすればいいか分からないので、
    CreatePlatformEndpointすることになるのかなと思いました。

    • suwa.yuki

      コメントいただきましてありがとうございます!
      返信が遅くなってしまい、申し訳ありません。

      複数のデバイスを登録する場合、デバイス毎にトークンは異なるので
      SNS上では別なEndpointとして取り扱います。
      そのため、本記事のEndpointの登録方法は各デバイスそれぞれ別で行います。

      多くのアプリの場合、ユーザー管理システム(データベース)を持っていると思いますので、
      「ひとりが複数のデバイスを持っている」ということを表現したいのであれば、
      データベースにレコードとして保存するように構築したほうが良いです。
      (例えば、ユーザーのレコードとEndpointのレコードを1:nで定義するなど)