memoirでPush通知の実装を行ったので、その構成について紹介
Pull Request
使用する技術
- GAE: Cloud Tasksのタスクを作成
- Cloud Tasks: Push通知のタスクを管理
- Cloud Functions: Push通知送信
- expo-notifications: Push通知送信に使用するライブラリ
Push通知はAPIと同期させる実行する必要が無かったので、以下の手順で実行させる - GAEからCloud Tasksのタスクを作成 → Cloud TasksでCloud Functionsの実行を管理 → Cloud FunctionsでPush通知を送信
実装
まず、Push通知を送信する Cloud Functions を作成
■ memoir-notification/function.go
package memoirnotification import ( "encoding/json" "net/http" expo "github.com/oliveroneill/exponent-server-sdk-golang/sdk" ) type NotificationRequest struct { Token []string `json:"token"` Title string `json:"title"` Body string `json:"body"` URLScheme string `json:"urlScheme"` } func SendNotification(w http.ResponseWriter, r *http.Request) { param := NotificationRequest{} if err := json.NewDecoder(r.Body).Decode(¶m); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } to := []expo.ExponentPushToken{} for _, token := range param.Token { pushToken, err := expo.NewExponentPushToken(token) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } to = append(to, pushToken) } client := expo.NewPushClient(nil) _, err := client.Publish( &expo.PushMessage{ To: to, Body: param.Body, Data: map[string]string{"urlScheme": param.URLScheme}, Sound: "default", Title: param.Title, Priority: expo.DefaultPriority, }, ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) }
Push通知は以下のExpo NotificationのGoのSDKを使用
実装はシンプルにNotificationのトークンとメッセージ内容とメッセージのパラメータをRequestパラメータに設定してPostするのみです。
こちらを以下のコマンドデプロイして、Cloud Functionsに関しては完了
$ gcloud functions deploy SendNotification --runtime go113 --trigger-http --region asia-northeast1
次にCloud Tasksの設定していきます。以下のコマンドでタスクのキューを作成
$ gcloud tasks queues create sendNotification
以下のコマンドで作成タスクの情報を確認
$ gcloud tasks queues describe sendNotification
以下みたいに表示されます
rateLimits: maxBurstSize: 100 maxConcurrentDispatches: 1000 maxDispatchesPerSecond: 500.0 retryConfig: maxAttempts: 100 maxBackoff: 3600s maxDoublings: 16 minBackoff: 0.100s state: RUNNING
設定はこちらから確認できます。
デフォルトだと、maxAttempts(最大試行回数): 100回、maxBackoff(リトライ間隔の最小の時間):0.1秒、maxBackoff(リトライ間隔の最大の時間):1時間、maxDoublings(最大倍数回数): 16回と、結構手厚い設定になっているので、今回のPush通知は必須の処理では無いので、以下のコマンドで設定を変更しました。
$ gcloud tasks queues update sendNotification --max-attempts 6 --min-backoff 5s --max-doublings 3
これでCloud Tasksの設定も完了です。 次はGAEからタスクを作成を実装していきます。
package task import ( "context" "encoding/json" "fmt" "os" cloudtasks "cloud.google.com/go/cloudtasks/apiv2" taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2" ) type NotificationRequest struct { Token []string `json:"token"` Title string `json:"title"` Body string `json:"body"` URLScheme string `json:"urlScheme"` } type HTTPTaskInterface interface { PushNotification(r NotificationRequest) (*taskspb.Task, error) } type HTTPTask struct { ProjectID string LocationID string QueueID string URL string } func NewNotificationTask() HTTPTaskInterface { return &HTTPTask{ ProjectID: os.Getenv("GCP_PROJECT_ID"), LocationID: os.Getenv("GCP_LOCATION_ID"), QueueID: os.Getenv("NOTIFICATION_QUEUE_ID"), URL: os.Getenv("NOTIFICATION_URL"), } } func (t *HTTPTask) PushNotification(r NotificationRequest) (*taskspb.Task, error) { ctx := context.Background() client, err := cloudtasks.NewClient(ctx) if err != nil { return nil, fmt.Errorf("NewClient: %v", err) } defer client.Close() body, err := json.Marshal(r) if err != nil { return nil, err } // Build the Task queue path. queuePath := fmt.Sprintf("projects/%s/locations/%s/queues/%s", t.ProjectID, t.LocationID, t.QueueID) req := &taskspb.CreateTaskRequest{ Parent: queuePath, Task: &taskspb.Task{ MessageType: &taskspb.Task_HttpRequest{ HttpRequest: &taskspb.HttpRequest{ HttpMethod: taskspb.HttpMethod_POST, Url: t.URL, Body: []byte(body), }, }, }, } createdTask, err := client.CreateTask(ctx, req) if err != nil { return nil, fmt.Errorf("cloudtasks.CreateTask: %v", err) } return createdTask, nil }
まず、Cloud Tasksで使用するGCPの情報を設定します。(URLには 上で作成したCloud FunctionsのURLを設定)
return &HTTPTask{ ProjectID: os.Getenv("GCP_PROJECT_ID"), LocationID: os.Getenv("GCP_LOCATION_ID"), QueueID: os.Getenv("NOTIFICATION_QUEUE_ID"), URL: os.Getenv("NOTIFICATION_URL"), }
そして、以下でタスクを作成しています。
req := &taskspb.CreateTaskRequest{ Parent: queuePath, Task: &taskspb.Task{ MessageType: &taskspb.Task_HttpRequest{ HttpRequest: &taskspb.HttpRequest{ HttpMethod: taskspb.HttpMethod_POST, Url: t.URL, Body: []byte(body), }, }, }, } createdTask, err := client.CreateTask(ctx, req) if err != nil { return nil, fmt.Errorf("cloudtasks.CreateTask: %v", err) }
これにより、タスクが作成されPush通知が送信されるようになります。 以下、動作確認の動画です