このチュートリアルでは、Firebase を使用したバックエンド データ ストレージやリアルタイム同期、ユーザー イベントロギングに対応するモバイルアプリの開発方法をご紹介します。Google Cloud Platform(GCP)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 にコピーできます。
目標
このチュートリアルでは、以下の手順を紹介します。
Android アプリである Playchat のビルド手順。このアプリはデータを Firebase Realtime Database に保存します。
App Engine フレキシブル環境での Java サーブレットの実行手順。このサーブレットは、Firebase に接続し、Firebase に保存されたデータが変更されたときに通知を受け取ります。
上記の 2 つのコンポーネントを使用して、分散型のストリーミング バックエンド サービスをビルドしてログデータを収集し、処理する手順。
料金
Firebase の使用は特定条件のもとで無料です。これらのサービスの使用量が Firebase 無料プランで指定された制限未満である場合、Firebase を使用しても料金はかかりません。
App Engine フレキシブル環境内のインスタンスについては、基盤となる Google Compute Engine 仮想マシンに対して費用がかかります。
始める前に
以下のソフトウェアをインストールします。
- Git
- Android Studio 4.0 以上
- Google API を使用して Android 8.1(API レベル 27)以上を実行するデバイスまたはエミュレータ
- 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-android-client
バックエンド サーブレットのコードのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/firebase-appengine-backend
アプリ用 SHA-1 フィンガープリントの生成
Google ログインでクライアント アプリを認証するには、証明書の SHA-1 フィンガープリントを用意する必要があります。このチュートリアルでは、デバッグ キーストアを使用します。キーストア フィンガープリントのリリース バージョンを作成する方法については、ユーザー独自のクライアントの認証をご覧ください。
使用するデバッグ キーストアの SHA-1 を構築します。
keytool -list -v \ -alias androiddebugkey -keystore ~/.android/debug.keystore
Firebase プロジェクトの作成
[プロジェクトを追加] をクリックします。
初めて登録する場合は、[プロジェクトを作成] をクリックします。
[プロジェクト名] に「
Playchat
」と入力します。残りの設定手順を行い、[プロジェクトの作成] をクリックします。
- このプロジェクトでは Google アナリティクスを有効にする必要はありません。
ウィザードでプロジェクトがプロビジョニングされたら、[続行] をクリックします。
プロジェクトの [プロジェクトの概要] ページで
(設定)をクリックし、[プロジェクトの設定] をクリックします。Android アイコンをクリックすると、[Android アプリに Firebase を追加する] ページが表示されます。
[Android パッケージ名] に「
com.google.cloud.solutions.flexenv
」と入力します。[デバッグ用の署名証明書 SHA-1] に、以前のセクションで生成した SHA-1 値を入力します。
[アプリの登録] をクリックします。
構成ファイルのダウンロードの手順に沿って、
google-services.json
ファイルをプロジェクトに追加します。[構成ファイルのダウンロード] で [次へ] をクリックします。
プロジェクト レベルとアプリレベルの
build.gradle
ファイルに対しての推奨される変更をメモします。[Firebase SDK の追加] で [次へ] をクリックします。
[コンソールに進む] をクリックして設定を完了します。
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>
Cloud Platform プロジェクトの課金と API の有効化
Cloud Platform でバックエンド サービスを実行するには、プロジェクトの課金と API を有効にする必要があります。Cloud Platform プロジェクトは、Firebase プロジェクトの作成で作成したプロジェクトと同じものであり、同じプロジェクト ID が付いています。
Cloud Platform Console で Playchat プロジェクトを選択します。
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the App Engine Admin API and Compute Engine API 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 が複数のサーブレット インスタンスを起動するためです。
Android アプリへの Firebase と Google Play 開発者サービスの追加
クライアント アプリは Firebase Realtime Database を使用して、メッセージの保存と同期や、ユーザーイベント ログの記録を行います。クライアント アプリは Google Play 開発者サービスを使用して、Google アカウントを所有するユーザーを認証します。
Android Studio で、[Tools] > [SDK Manager] を選択します。
[SDK Tools] タブを選択します。
[Google Play services] がまだ選択されていない場合は選択します。
[OK] をクリックしてインストールします。
[File] > [Open…] の順に選択して、
firebase-android-client
ディレクトリを選択します。Gradle プロジェクト情報にビルドの終了が表示されるまで待ちます。Gradle ラッパーを使用するように求められたら、[OK] をクリックします。
Firebase プロジェクトの作成でメモした、プロジェクト レベルとアプリレベルの
build.gradle
ファイルの変更内容が、サンプルコードにすでに反映されています。
Android アプリの実行とテスト
Android Studio で
firebase-android-client
プロジェクトを開き、[Run] > [Run 'app'] を選択します。Google API を使用して Android 6.0 を実行するデバイスまたはエミュレータをテストデバイスとして選択します。
アプリがデバイスに読み込まれたら、Google アカウントでログインします。
PlayChat のタイトルの左にあるメニューをクリックし、[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 の下にも、記録されたイベントのログエントリが表示されます。
コードの確認
Android アプリである Playchat は、FirebaseLogger
というクラスを定義します。これは、Firebase Realtime Database にユーザーイベント ログを書き込むために使用されます。
新しいユーザーがログインすると、Playchat は requestLogger
関数を呼び出して Firebase Realtime Database の /inbox/
に新しいエントリを追加します。また、リスナーを設定して、サーブレットが割り当てを受け入れてエントリの値を更新したときに、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 アカウントに課金されないようにする手順は次のとおりです。Cloud Platform と Firebase プロジェクトを削除する
課金を停止する最も簡単な方法は、このチュートリアルで作成したプロジェクトを削除することです。Firebase コンソールでプロジェクトを作成した場合でも、Cloud Platform Console でプロジェクトを削除できます。なぜなら、Firebase プロジェクトと Cloud Platform プロジェクトは同一であるからです。
- 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 アプリのデフォルト以外のバージョンを削除する
Cloud Platform プロジェクトと 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 アプリに音声翻訳機能を追加するをご覧ください。