
#21 スマートフォンPush通知の配信基盤 — APNs/FCMとGaurun
アプリを開いていないのに、スマートフォンに通知が届く。普段は当たり前に使っているこの仕組みは、裏側ではどう動いているのでしょうか。今回はメルカリが公開し、すでにGitHub上でアーカイブされたGo製のプッシュ通知サーバー「Gaurun」を入口に、元メンテナのcatatsuyがスマートフォンのプッシュ通知の仕組みを掘り下げます。 話はまず、iPhoneやAndroidにプッシュ通知が届く仕組みから。端末がAppleやGoogleのサーバーと張り続けているコネクションの話や、APNsが5223番、FCMが5228番というHTTPSとは別のポートを使っている話をhentekoが素朴に掘り下げます。そこからAPNsへ。HTTP/2しか話せずHTTP/1.1にフォールバックしない、コネクションを維持しながら大量の通知を送る必要がある、100万人に送るなら基本的には100万の配送先を扱う必要がある、といった独特の事情から、PHPのWebアプリケーションから直接大量のプッシュ通知を送るのが厳しく、Gaurunのような専用サーバーが必要だった背景が見えてきます。 さらに話はGoのHTTP/2実装まで降りていきます。APNsの証明書認証のためにTLSClientConfigを設定するとHTTP/2が自動で有効にならず、golang.org/x/net/http2を明示的に使う必要があった時代がありました。しかしGo本体にもHTTP/2の実装がbundleされているため、異なるバージョンのHTTP/2実装が同居し、実際の障害につながります。最終的にはGo 1.13で追加されたForceAttemptHTTP2によって、Gaurunからgolang.org/x/net/http2への直接依存を外せるようになりました。Appleの証明書更新とJWTを使ったトークンベース認証など、「プッシュ通知を送る」裏側にある実装と運用の面倒さも語られます。 後半は「今から作るなら」の話。現在はFCMからAPNsへ通知を送ることもでき、Topicを使えば大量のfan-outまでGoogle側に任せられます。Gaurunが自前で担っていた仕事のかなりの部分を、今ではFCMがManaged Serviceとして引き受けています。 実はcatatsuy自身も、GaurunをFCM HTTP v1 APIに対応させるPull Requestを出していました。しかし当時はLegacy APIがまだ使えており、急いで移行する理由が薄かったためclose。その後Legacy APIは終了し、Gaurunもアーカイブされました。外部サービスのAPIの変化によって、OSSの役割がどう変わっていくのかという話にもつながります。 さらに、既存のAPNs tokenをFCM側へ移す仕組み、registration tokenからFIDへの移行、中国のグレートファイアウォールといった少し厄介なケースまで掘り下げます。 プッシュ通知を実装したことがある人はもちろん、「そもそもスマートフォンの通知ってどうやって届いているの?」という人にも聞いてほしい回です。 ## 関連リンク - Gaurun (mercari/gaurun): https://github.com/mercari/gaurun - gaurunとGoのHTTP/2事情について: https://engineering.mercari.com/blog/entry/2019-12-13-110000/ - Gaurun FCM HTTP v1 API 対応 PR #109: https://github.com/mercari/gaurun/pull/109 - Gaurun ForceAttemptHTTP2 対応 PR #130: https://github.com/mercari/gaurun/pull/130 - Gaurun APNs トークンベース認証対応 PR #138: https://github.com/mercari/gaurun/pull/138 - Apple Push Notification service (APNs) - Sending notification requests to APNs: https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns - APNs - Establishing a token-based connection: https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns - APNs - Establishing a certificate-based connection: https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns - Firebase Cloud Messaging (FCM): https://firebase.google.com/docs/cloud-messaging - FCM HTTP v1 API リファレンス: https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages - FCM トピックメッセージング: https://firebase.google.com/docs/cloud-messaging/topic-messaging - Firebase Admin SDK: https://firebase.google.com/docs/admin/setup - Instance ID API(APNs トークンの FCM トークンへのインポート): https://developers.google.com/instance-id/reference/server - golang.org/x/net/http2: https://pkg.go.dev/golang.org/x/net/http2 - Go net/http Transport(ForceAttemptHTTP2): https://pkg.go.dev/net/http#Transport - Go: https://go.dev/ - nginx: https://nginx.org/ ───────────── YouTube: https://youtu.be/l5N_fS0527g Web: https://yaoyorozu-oss.henteko07.com/ X: https://x.com/yaoyorozu_oss