Mokelab Blog

#35 GoでFirebase Cloud Messaging(FCM)のサーバー環境を作る

Firebase Cloud Messaging(FCM)は、Android / iOS / Webにプッシュ通知を送信するためのサービスです。今回は通知を送信するサーバー側をGoで実装する方法を紹介します。

公式ドキュメントはこちらにあります。

ライブラリの追加

firebase.google.com/goを追加します。

$ go get firebase.google.com/go

秘密鍵の入ったJSONを取得する

以前はサーバーキーだけでFCMサーバーにリクエストを投げることができましたが、v1 APIでは認証にJWT を使っているようです。署名の作成に秘密鍵が必要なので、これを FirebaseのWebコンソールから取得します。

「プロジェクトを設定」→「サービスアカウント」で、新しい秘密鍵を作成します。ここではkey.json というファイル名で保存したとします。

デバイス登録トークンを使って送信する

送信先情報となるデバイス登録トークンは、何らかの方法でモバイルアプリ側から取得できているものとします。

コードは次のような感じになります。

package main

import (
	"context"
	"fmt"

	"math/rand"
	"time"

	firebase "firebase.google.com/go"
	"firebase.google.com/go/messaging"
	"google.golang.org/api/option"
)

const maxRetry = 3

func main() {
	ctx := context.Background()
	// クライアントオブジェクトを作って
	client, err := makeClient(ctx)
	if err != nil {
		fmt.Printf("Error: %s", err)
		return
	}

	// 送信内容を作って
	message := makeMessage("token1234", "Hello")

	// 指数バックオフで送信。とりあえず3回くらいはリトライしてみる
	var response string
	for i := 0; i < maxRetry; i++ {
		response, err = client.Send(ctx, message)
		if err == nil {
			break
		}
		// 待ち時間はどんどん倍にしていく
		waitTime := (1<<i)*1000 + rand.Intn(1000)
		time.Sleep(time.Duration(waitTime) * time.Millisecond)
	}
	if err != nil {
		fmt.Printf("Failed to send message: %s", err)
		return
	}
	fmt.Printf("response=%s\n", response)
}

token1234の部分がデバイス登録トークンです。

クライアントを作る

makeClient()の定義は次のようにします。サーバー起動時に1つだけクライアントオブジェクトを作成すればよいでしょう。

func makeClient(ctx context.Context) (*messaging.Client, error) {
	opt := option.WithCredentialsFile("key.json")

	app, err := firebase.NewApp(ctx, nil, opt)
	if err != nil {
		return nil, err
	}

	return app.Messaging(ctx)
}

メッセージを作る

makeMessage()の定義は次のようにします。用途にあわせて引数を追加したりしましょう。

func makeMessage(token, body string) *messaging.Message {
	return &messaging.Message{
		Token: token,
		Notification: &messaging.Notification{
			Body: body,
		},
		Android: &messaging.AndroidConfig{},
		APNS:    &messaging.APNSConfig{},
		Webpush: &messaging.WebpushConfig{},
	}
}

指数バックオフで送信する

最後に送信する部分です。送信自体はクライアントのSend()メソッドを呼ぶだけです。 FCMサーバーがエラーを返した場合、即リトライするのではなく、指数バックオフでリトライするようにします。

var response string
for i := 0; i < maxRetry; i++ {
	response, err = client.Send(ctx, message)
	if err == nil {
		break
	}
	// 待ち時間はどんどん倍にしていく
	waitTime := (1<<i)*1000 + rand.Intn(1000)
	time.Sleep(time.Duration(waitTime) * time.Millisecond)
}
if err != nil {
	fmt.Printf("Failed to send message: %s", err)
	return
}

まとめ

GoでFCMにリクエストを送信し、Push通知する方法の概要を紹介しました。レガシーAPIを使っている方は これを機会に、v1 API版を使用してみましょう。

コラム:google.golang.org/api

今回はfirebase.google.com/goを使用しましたが、なんと世の中には もうひとつFCMとやりとりするライブラリがgoogle.golang.org/apiとして公開されています。

GoDocはこちらです。 ライセンスを見るとこちらはGoogleになっています。

通常、ほぼ同じ機能を提供するライブラリが2つ以上存在した場合、 どちらかの開発が止まっていたりすることが多いのですが、 この2つは本記事執筆時点でどちらもアクティブに 開発が行われているようです。

なぜこのような事態になっているかご存知の方がいれば、@mokelabに教えてください。

本サイトではサービス向上のため、Google Analyticsを導入しています。分析にはCookieを利用しています。