ぺい

大阪の専門学生の落書き。主にエンジニア寄りの話。

builderscon 2017 Tokyo: Building high performance push notification server in Go

builderscon.io

github.com

GoのOSSのGaurunを使ってアプリユーザーにプッシュ通知を送るための話。
※聞きながら書いたので、雑になってます

プッシュ通知の仕組み

Push通知のためのサービスを通して、ユーザーに通知が行く。
APNs GCM/FCMなどを使った話をします。

APNs

種類

  • APNs Binary Provider API(レガシー)
  • APNs Provider API これだと、JSONペイロードcurlで投げるだけでプッシュ通知を試せたりするくらい気軽になる。

GCM/FCM

  • HTTPS
  • サーバーキー

GoogleはFCM使えと言っている。
基本はJSONペイロードcurlで投げるだけでpush通知を投げれる。

ネットワークを介す処理だから遅い

APNsとGCM/FCMとの通信には、ある程度時間がかかる。これはサーバーがアメリカにあったりするからです。特にAPNsは結構遅い。

構成例

サーバー -> nginx -> Gaurun -> HTTP2 -> APNs/GCMなど -> アプリ

通知するタイミングは、お気に入りつけたや購入したコメントしたなど。
出来れば短時間で対象ユーザーに送りたい。

短時間で大量にさばくには、ネットワークレイテンシが高い問題を解消する必要があった。(これが難しい)

旧メルカリの構成

通知が遅くなる問題を見たら、処理と通知が同期的に行われていたから、ネットワークレイテンシと比例して遅くなる。またバッチ処理もあまり効率が良くなかったので、全ユーザー通知も時間がかかっていた。

通知を非同期にした

APIサーバーが通知Queueに送って、それらを順次処理していく形にした。これで劇的なパフォーマンスの改良が可能になった。QueueはQ4Mを使った。

PHPがそもそも遅いので限界があった

PHPは並行処理が苦手なので、そこまで速くならなかった。

Goと相性が良かった

ネットワークレイテンシを下げる必要がある。スループットを改善する。このような需要とマッチしていた。

Gaurun

プッシュ通知サーバーに使うGoOSS
JSON baseのAPI(HTTP)

提供しているAPI

  • プッシュ通知
  • モニタリング(errorの数やgoroutineの数)
  • コンフィグ

Goを採用した理由

  • 高い並行性
  • 高いパフォーマンス
  • 標準パッケージが使いやすい

構成

  • HTTPサーバー
  • APNs GCM/FCMのプロキシ
  • queueと並行処理が出来る

gorutineはwerkerはpusherを持っている。
werkerはpusherの数しか知らない。pusherはpushするだけ。

コネクションハンドリング

http.Clientを使っている(net/http) 設定は、http.Transportを使っている。昔は結構自力で書いてたけど、いまは標準パッケージにお任せしている。

Timeoutについて

サーバーのタイムアウトなのか?クライアントのタイムアウトなのか?接続する時のタイムアウトなのか?とにかく色んなタイムアウトがある。

net/httpパッケージ

  • net.Dial
  • http.Transport
  • http.Client
  • http.Server

上記それぞれにTimeoutが用意されている

Gaurunのパフォーマンスを上げる

TOMLで設定をチューニングする必要があります。なぜなら、初期設定のままだと、結構保守的な設定になっています。

  • core.workers ワーカーの数
  • core.queues channelのサイズ

などなど、色々な設定が出来る。

pusherの数

core.workers x core.pusher_max を調整して、メモリを無駄なく使うと良い。

Keep-Aliveの数

keep-aliveはあまりにも大きすぎる数値を指定すると、場合によってはAPNsなどに拒否されることがあったりするかも?

Bulk enqueue

JSONペイロードのnotification[]の中に複数指定して、リクエストすればおk。デフォルトでは、100件までとしている。

Device tokenのスクリーニング

無効化されたtokenを定期的に削除したい。無駄に送信するとパフォーマンスが落ちるので大事です。Gaurunでは成功、失敗が error_logで取れるので、これを使えばスクリーニングが出来ます。

メルカリでの実際の運用
JSON.log -> S3 -> Batch -> MySQL