このチュートリアルでは、Firebase を使用したバックエンド データ ストレージやリアルタイム同期、ユーザーイベント ロギングに対応する iOS アプリの開発方法をご紹介します。App Engine フレキシブル環境で動作する Java サーブレットが、Firebase に保存された新しいユーザーログをリッスンして処理します。
このチュートリアルの手順では、Firebase と App Engine フレキシブル環境を使用して目的のモバイルアプリを開発する方法を説明します。
ユーザーデータの処理またはイベントの統合がアプリ側で必要な場合は、App Engine フレキシブル環境を利用して Firebase を拡張すれば、リアルタイムの自動データ同期が利用できます。
Playchat サンプルアプリによりチャット メッセージが Firebase Realtime Database に保存され、デバイス間で自動的に同期されます。Playchat は、ユーザー イベントログも Firebase に書き込みます。データベースによるデータの同期方法の詳細については、Firebase ドキュメントの仕組みをご覧ください。
次の図に、Playchat クライアントのアーキテクチャを示します。
App Engine フレキシブル環境で動作する一連の Java サーブレットが、リスナーとして Firebase に登録されます。これらのサーブレットが新しいユーザーイベント ログに応答し、ログデータを処理します。サーブレットはトランザクションを使用します。これにより、各ユーザーイベント ログを処理するサーブレットがただ 1 つであることが保証されます。
次の図に、Playchat サーバーのアーキテクチャを示します。
アプリとサーブレット間の通信は、以下の 3 つの処理で発生します。
新しいユーザーが Playchat にログインし、アプリがそのユーザーのロギング サーブレットをリクエストするために Firebase Realtime Database の
/inbox/
の下にエントリを追加したとき。いずれかのサーブレットが、このエントリの値を自分のサーブレット ID に変更して割り当てを受け入れたとき。このサーブレットは、自分以外のサーブレットが値を変更できないことを保証するために Firebase トランザクションを使用します。値が更新されると、他のすべてのサーブレットはリクエストを無視します。
ユーザーがログインまたはログアウトするか、新しいチャネルに切り替えたとき。このアクションを Playchat が
/inbox/[SERVLET_ID]/[USER_ID]/
に記録します。ここで、[SERVLET_ID]
はサーブレット インスタンスの ID であり、[USER_ID]
はユーザーを表すハッシュ値です。サーブレットが Inbox を監視して新しいエントリのログデータを収集するとき。
このサンプルアプリでは、ログデータがサーブレットによりローカルでコピーされ、ウェブページに表示されます。このアプリの本番環境バージョンでは、ログデータをサーブレットで処理する目的や、保存および分析の目的で Cloud Storage、Cloud Bigtable、BigQuery にコピーできます。
目標
このチュートリアルでは、以下の手順を紹介します。
iOS アプリ Playchat を作成する。このアプリはデータを Firebase Realtime Database に保存します。
App Engine フレキシブル環境で Java サーブレットを実行する。このサーブレットは Firebase に接続し、Firebase に格納されているデータが変更されたとき通知を受け取ります。
上記の 2 つのコンポーネントで分散型のストリーミング バックエンド サービスを構築してログデータを収集し処理する。
費用
Firebase の使用は特定条件のもとで無料です。これらのサービスの使用量が Firebase 無料プランで指定された制限未満である場合、Firebase を使用しても料金はかかりません。
App Engine フレキシブル環境内のインスタンスについては、基盤となる Compute Engine 仮想マシンに対して費用がかかります。
始める前に
以下のソフトウェアをインストールします。
- Git
- Xcode 9
- CocoaPods
- Apache Maven 3.6.x 以上
- Java 8 以上
- Google Cloud CLI
ターミナル ウィンドウで次のコマンドを実行して、gcloud CLI の App Engine Java コンポーネントをインストールします。
gcloud components install app-engine-java
サンプルコードのクローンの作成
クライアント アプリのコードのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/firebase-ios-samples
バックエンド サーブレットのコードのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/firebase-appengine-backend
Firebase プロジェクトの作成
[プロジェクトを追加] をクリックします。
[プロジェクト名] に「
Playchat
」と入力します。プロジェクトに割り当てられたプロジェクト ID は、このチュートリアルの複数の手順で使用するのでメモしておきます。残りの設定手順を行い、[プロジェクトを作成] をクリックします。
ウィザードでプロジェクトがプロビジョニングされたら、[続行] をクリックします。
プロジェクトの概要ページで「設定」の歯車をクリックし、[プロジェクトの設定] をクリックします。
[iOS アプリに Firebase を追加] をクリックします。
[iOS バンドル ID] に「
com.google.cloud.solutions.flexenv.PlayChat
」と入力します。[アプリの登録] をクリックします。
[構成ファイルをダウンロード] セクションの手順に従い、プロジェクトの
PlayChat
フォルダにGoogleService-Info.plist
ファイルを追加します。[設定ファイルのダウンロード] で [次へ] をクリックします。
CocoaPods の使用方法をメモし、プロジェクトの依存関係をインストールして管理します。サンプルコードでは、CocoaPods 依存関係マネージャーがすでに構成されています。
次のコマンドを実行して、依存関係をインストールします。コマンドが完了するまでに時間がかかる場合があります。
pod install
CocoaPods のインストールで Firebase の依存関係が見つからない場合は、
pod repo update
を実行する必要がある場合があります。このステップが完了したら、この iOS アプリの以降のすべての開発に、新たに作成された
.xcworkspace
ファイルを.xcodeproj
ファイルの代わりに使用します。[Firebase SDK の追加] で [次へ] をクリックします。
プロジェクトで Firebase の初期化に必要なコードをメモします。
[初期化コードの追加] セクションで、[次へ] をクリックします。
[アプリを実行してインストールを確認] セクションで [このステップをスキップ] をクリックします。
Realtime Database の作成
Firebase コンソールでプロジェクトを選択します。
コンソールの左側のメニューで、[構築] グループの [Realtime Database] を選択します。
[Realtime Database] セクションで [データベースを作成] をクリックします。
付近のロケーションを選択します。
[セキュリティ ルール] ダイアログで [テストモードで開始する] を選択し、[有効にする] をクリックします。
この操作により、Firebase に保存したデータが表示されます。このチュートリアルの後のステップで、ウェブページを再度閲覧し、クライアント アプリとバックエンド サーブレットにより追加、変更されたデータを確認します。
データベースの [ルール] タブで、UNIX エポック時刻で定義された時間単位で 30 日先の日付を指定する読み取り/書き込みのセキュリティ ルールがあることを確認します。たとえば、8 月 4 日にルールを作成した場合、ルールは 9 月 4 日以降無効になります。
{ "rules": { ".read": "now < 1659639249000", //2022-08-04 ".write": "now < 1659639249000" //2022-08-04 } }
プロジェクトの Firebase URL をメモします。これは、リンクアイコンの横に
https://[FIREBASE_PROJECT_ID].firebaseio.com/
という形式で表示されています。
Firebase プロジェクトの Google 認証の有効化
Firebase プロジェクトに接続するために、さまざまなログイン プロバイダを構成できます。このチュートリアルでは、ユーザーが Google アカウントを使用してログインできるように認証を設定する方法について説明します。
Firebase コンソールの左側のメニューで、[構築] グループの [Authentication] を選択します。
初めてこのページにログインする場合は、[ログイン方法を設定] または [使ってみる] をクリックします。
[ログイン方法] タブで [Google] を選択します。[有効にする] トグルをオンに切り替えてサポートメールを選択し、[保存] をクリックします。
Firebase プロジェクトへのサービス アカウントの追加
バックエンド サーブレットでは、ログインに Google アカウントを使用しません。代わりに、サービス アカウントを使用して Firebase に接続します。以下の手順では、Firebase に接続するサービス アカウントを作成して、サービス アカウントの認証情報をサーブレットのコードに追加する方法について説明します。
Firebase コンソールの左側のメニューで、Playchat プロジェクト ホームの横にある [設定] の歯車アイコンを選択し、[プロジェクトを設定] を選択します。
[サービス アカウント] を選択し、[サービス アカウント権限の管理] リンクをクリックします。
[サービス アカウントを作成] をクリックします。
以下の設定を構成します。
- [サービス アカウント名] に「
playchat-servlet
」と入力し、[作成して続行] をクリックします。 [ロールを選択] で、[プロジェクト] > [オーナー] の順に選択し、[続行] をクリックします。
[完了] をクリックします。
- [サービス アカウント名] に「
作成したサービス アカウントをクリックし、[鍵] タブで [鍵を追加]、[新しい鍵を作成] の順にクリックします。
[キーのタイプ] の横にある [JSON] をクリックし、[作成] をクリックしてキーをダウンロードします。
サービス アカウント用のダウンロードした JSON キーファイルを、
src/main/webapp/WEB-INF/
ディレクトリのバックエンド サービス プロジェクトfirebase-appengine-backend
に保存します。ファイル名の形式はPlaychat-[UNIQUE_ID].json
です。src/main/webapp/WEB-INF/web.xml
を編集して、初期化パラメータを以下のように設定します。JSON_FILE_NAME
を、ダウンロードした JSON キーファイルの名前に置き換えます。FIREBASE_URL
を、以前にメモした Firebase URL で置き換えます。<init-param> <param-name>credential</param-name> <param-value>/WEB-INF/JSON_FILE_NAME</param-value> </init-param> <init-param> <param-name>databaseUrl</param-name> <param-value>FIREBASE_URL</param-value> </init-param>
Google Cloud プロジェクトの課金と API を有効にする
GCP でバックエンド サービスを実行するには、プロジェクトの課金と API を有効にする必要があります。Cloud プロジェクトは、Firebase プロジェクトの作成で作成したものと同じプロジェクトで、同じプロジェクト ID を持ちます。
Google Cloud Console で、Playchat プロジェクトを選択します。
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the App Engine Admin and Compute Engine APIs.
バックエンド サービスのビルドとデプロイ
このサンプルでは、バックエンド サービスは Docker 構成を使用してホスティング環境を指定します。このようにカスタマイズするには、App Engine スタンダード環境ではなく App Engine フレキシブル環境を使用する必要があります。
バックエンド サーブレットを構築して App Engine フレキシブル環境にデプロイするには、Google App Engine Maven プラグインを使用できます。このプラグインは、このサンプルに含まれている Maven ビルドファイルですでに指定されています。
プロジェクトの設定
Maven でバックエンド サーブレットが正しくビルドされるようにするには、サーブレットのリソースを起動する Google Cloud Platform(GCP)プロジェクトを Maven に指定する必要があります。GCP プロジェクトと Firebase プロジェクトの ID は同じです。
gcloud CLI が GCP へのアクセスに使用する認証情報を入力します。
gcloud auth login
次のコマンドを実行して、プロジェクトを Firebase プロジェクトに設定します。[FIREBASE_PROJECT_ID] は、以前にメモした Firebase プロジェクト ID に置き換えます。
gcloud config set project [FIREBASE_PROJECT_ID]
構成を表示して、プロジェクトが設定されたことを確認します。
gcloud config list
App Engine を初めて使用する場合は、App Engine アプリを初期化します。
gcloud app create
(省略可)ローカル サーバーでのサービスの実行
新しいバックエンド サービスを開発する際には、App Engine にデプロイする前にローカルでそのサービスを実行するのが有効です。これにより、変更の迅速な反復適用が可能になり、App Engine にデプロイする際のオーバーヘッドが回避されます。
ローカルでサーバーを実行する場合、Docker の構成は使用されず、App Engine 環境では実行されません。代わりに、Maven がすべての依存関係ライブラリをローカルにインストールし、Jetty ウェブサーバー上でアプリが実行されます。
firebase-appengine-backend
ディレクトリで、次のコマンドを使用してバックエンド モジュールをローカルで構築して実行します。mvn clean package appengine:run
~/google-cloud-sdk
以外のディレクトリに Google Cloud CLI をインストールした場合は、次に示すようにコマンドにインストール パスを追加します([PATH_TO_TOOL]
の部分をカスタムパスで置き換えてください)。mvn clean package appengine:run -Dgcloud.gcloud_directory=[PATH_TO_TOOL]
「アプリケーション "Python.app" で受信ネットワーク接続を受け入れますか?」というメッセージが表示された場合は、[Allow] を選択します。
デプロイの終了後、http://localhost:8080/printLogs を開いて、バックエンド サービスが実行されていることを確認します。ウェブページに、「Inbox :」に続いて 16 桁の ID が表示されます。これは、ローカルマシンで実行されているサーブレットの Inbox ID です。
ページを更新してもこの ID は変わりません。ローカル サーバーは単一のサーブレット インスタンスを起動するからです。これはテストの際に役立ちます。Firebase Realtime Database にはサーブレット ID が 1 つしか保存されないためです。
ローカル サーバーをシャットダウンするには、Ctrl+C と入力します。
App Engine フレキシブル環境へのサービスのデプロイ
App Engine フレキシブル環境でバックエンド サービスを実行すると、App Engine は /firebase-appengine-backend/src/main/webapp/Dockerfiles
の構成を使用して、サービスが実行されるホスティング環境を構築します。フレキシブル環境は複数のサーブレット インスタンスを起動し、需要に応じてその規模を拡大または縮小します。
firebase-appengine-backend
ディレクトリで、次のコマンドを使用してバックエンド モジュールをローカルで構築して実行します。mvn clean package appengine:deploy
mvn clean package appengine:deploy -Dgcloud.gcloud_directory=[PATH_TO_GCLOUD]
ビルドを実行すると、「Sending build context to Docker daemon…」と表示されます。上述のコマンドにより、Docker の構成がアップロードされ、App Engine フレキシブル環境に設定されます。
デプロイが終了したら、https://[FIREBASE_PROJECT_ID].appspot.com/printLogs
を開きます。ここで、[FIREBASE_PROJECT_ID]
は Firebase プロジェクトの作成で割り当てられた ID です。ウェブページに、「Inbox :」に続いて 16 桁の ID が表示されます。これは、App Engine フレキシブル環境で実行されているサーブレットの Inbox ID です。
ページを更新すると、この ID は定期的に変わります。これは、受信したクライアントからのリクエストを処理するために、App Engine が複数のサーブレット インスタンスを起動するためです。
iOS サンプルの URL スキームの更新
Xcode で
PlayChat
ワークスペースを開き、PlayChat
フォルダを開きます。GoogleService-Info.plist
を開き、REVERSED_CLIENT_ID
の値をコピーします。Info.plist
を開いて、[key URL types] > [Item 0 (Editor)] > [URL Schemes] > [Item 0] の順に移動します。プレースホルダ値 [REVERSED_CLIENT_ID] を
GoogleService-Info.plist
からコピーした値に置き換えます。
iOS アプリの実行とテスト
Xcode で
PlayChat
ワークスペースを開き、[Product] > [Run] の順に選択します。アプリがシミュレータに読み込まれたら、Google アカウントでログインします。
[books] チャンネルを選択します。
メッセージを入力します。
入力すると、Playchat アプリがメッセージを Firebase Realtime Database に保存します。Firebase はデータベースに保存されたデータをデバイス間で同期します。Playchat を実行するデバイスでは、ユーザーが [books] チャンネルを選択すると新しいメッセージが表示されます。
データの検証
Playchat アプリを使ってユーザー イベントをいくつか生成した後に、サーブレットがリスナーとして登録され、ユーザーイベント ログが収集されていることを確認します。
アプリの Firebase Realtime Database を開きます。ここで、[FIREBASE_PROJECT_ID]
は Firebase プロジェクトの作成で割り当てられた ID です。
https://console.firebase.google.com/project/[FIREBASE_PROJECT_ID]/database/data
Firebase Realtime Database の最下部(/inbox/
の下)に、接頭辞 client-
の付いたノードのグループが表示され、その後に、ユーザー アカウントのログインを表すランダム生成のキーが続きます。この例の最後の項目 client-1240563753
の後には、ユーザーのログイベントを現在リッスンしているサーブレットを示す 16 桁の ID が続きます(この例では、0035806813827987
)。
すぐ上の、/inbox/
データの場所の下に、現在割り当てられているすべてのサーブレットのサーブレット ID が表示されます。この例では、ログを収集しているサーブレットは 1 つだけです。/inbox/[SERVLET_IDENTIFIER]
の下には、アプリによりそのサーブレットに書き込まれたユーザーログが示されます。
バックエンド サービスの App Engine ページ(https://[FIREBASE_PROJECT_ID].appspot.com/printLogs
)を開きます。ここで、[FIREBASE_PROJECT_ID]
は Firebase プロジェクトの作成で割り当てられた ID です。このページには、生成されたユーザー イベントを記録するサーブレットの ID が表示されます。また、そのサーブレットの Inbox ID の下にも、記録されたイベントのログエントリが表示されます。
コードの確認
Playchat iOS アプリは、FirebaseLogger
というクラスを定義します。これは、Firebase Realtime Database にユーザーイベント ログを書き込むために使用されます。
新しいユーザーがログインすると、Playchat は requestLogger
関数を呼び出して Firebase Realtime Database の /requestLogger/
に新しいエントリを追加します。また、リスナーを設定して、サーブレットが割り当てを受け入れてエントリの値を更新したときに、Playchat が応答できるようにします。
サーブレットが値を変更すると、Playchat はリスナーを削除して、「Signed in」のログをサーブレットの Inbox に書き込みます。
バックエンド サービス側では、サーブレット インスタンスが起動されると、MessageProcessorServlet.java
の init(ServletConfig config)
関数が Firebase Realtime Database に接続し、リスナーを /inbox/
データのロケーションに追加します。
新しいエントリが /inbox/
データのロケーションに追加されると、サーブレットはその値を自身の ID に変更します。また、そのユーザーログの処理の割り当てをサーブレットが受け入れたことを示す信号が Playchat アプリに送信されます。サーブレットは Firebase のトランザクションを使用することで、値を変更して割り当てを受け入れるサーブレットがただ 1 つであることを保証します。
サーブレットは、ユーザーのイベントログ処理の割り当てを受け入れた後、Playchat アプリが新しいログファイルをサーブレットの Inbox に書き込んだことを検出するリスナーを追加します。Inbox への書き込みを検出すると、サーブレットは Firebase Realtime Database から新しいログデータを取得します。
クリーンアップ
このチュートリアルで使用するリソースについて、Google Cloud Platform アカウントに課金されないようにする手順は次のとおりです。Google Cloud Platform と Firebase のプロジェクトの削除
課金を停止する最も簡単な方法は、このチュートリアルで作成したプロジェクトを削除することです。Firebase コンソールでプロジェクトを作成しましたが、Firebase プロジェクトと Cloud プロジェクトは同一であるため、Google Cloud コンソールでも削除できます。
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
App Engine アプリのデフォルト以外のバージョンを削除する
GCP プロジェクトと Firebase プロジェクトを削除しない場合は、App Engine フレキシブル環境のデフォルト バージョンではないアプリを削除すれば、課金をいくらか減らすことができます。
- In the Google Cloud console, go to the Versions page for App Engine.
- Select the checkbox for the non-default app version that you want to delete.
- アプリのバージョンを削除するには、[ 削除] をクリックします。
次のステップ
データの解析とアーカイブ - このサンプルでは、サーブレットはログデータをメモリ内のみに保存します。このサンプルの機能を拡張するには、Cloud Storage、Cloud Bigtable、Google Cloud Dataflow、および BigQuery などのサービスを使用して、サーブレットによってデータがアーカイブ、変換、分析されるようにします。
サーブレット間での負荷の均等分散 - App Engine では、自動スケーリングと手動スケーリングの両方が使用できます。自動スケーリングでは、フレキシブル環境でワークロードの変化が検出されると、クラスタで VM が追加または削除されます。手動スケーリングでは、トラフィックを処理するインスタンスの数を固定値で指定します。スケーリングの構成方法については、App Engine ドキュメントのサービス スケーリング設定をご覧ください。
ユーザー アクティビティ ログは、Firebase Realtime Database にアクセスしてサーブレットに割り当てられるため、ワークロードが均等に分散されないことがあります。たとえば、特定のサーブレットが他のサーブレットよりも多くのユーザーイベント ログを処理する場合があります。
それぞれの VM のワークロードを個別に制御するワークロード マネージャーを実装すれば効率が良くなります。このようなワークロード分散処理は、秒単位のリクエストのロギングや、同時クライアント数の監視といった指標に基づいて実行されます。
未処理のユーザーイベント ログの回復 - このサンプルの実装内容では、サーブレット インスタンスがクラッシュしても、そのインスタンスに関連付けられたクライアント アプリは、ログイベントを Firebase Realtime Database のサーブレットの Inbox に送信し続けます。アプリの本番環境バージョンでは、バックエンド サービスでこの状況を検出して、未処理のユーザー イベント ログを回復する必要があります。
Cloud AI プロダクトを使用した追加機能の実装 - Cloud AI プロダクトおよびサービスを使用して ML ベースの機能を実装する方法です。たとえば、このサンプルの実装内容を拡張して、Speech-to-Text API、Translation API、Text-to-Speech API を組み合わせた音声翻訳機能を実装できます。詳しくは、Android アプリに音声翻訳機能を追加するをご覧ください。