marketplace.2016-11-08ハイパフォーマンスGaurun〜メルカリの大規模プッシュ配信を支えるミドルウェア〜SREチームのcubicdaiyaです。今回は本ブログでも何度か紹介しているGaurunを利用したメルカリのプッシュ配信基盤とGaurunのパフォーマンスを最大化する方法について紹介します。github.com改めて紹介するとGaurunはスマホアプリ向けのプッシュ通知サーバです。JSONベースのAPIでラップして大量のプッシュ通知を素早く送信することができるのが特徴です。メルカリのプッシュ配信基盤メルカリのプッシュ配信基盤はnginxによるL7ロードバランサーとGaurunで構成されています。JSONベースのAPIを利用してiOSやAndroidの端末へのプッシュ通知に必要な情報(端末の種類、トークン等)を送ります。Gaurunは送られたリクエストボディを元にAPNsやGCMのプロトコルに合わせたリクエストを生成し、プッシュ通知を行います。プッシュ通知処理をAPIサーバやバッチサーバで行わずにGaurunに中継して行うのは以下の理由からです。プッシュ通知処理によるレイテンシの最小化Gaurunはリクエストを受け取ったらクライアントに対して即座にレスポンスを返し、プッシュ通知自体は非同期で行うのでクライアント側のプッシュ通知処理で発生するレイテンシを最小化することができます。APIサーバではユーザに素早くレスポンスを返す必要があるのでレイテンシを最小化することは非常に重要です。一斉プッシュ配信のスループット向上プッシュ通知のようにネットワークレイテンシが大きい処理を一斉に行う場合、単体のバッチプログラムだけで高いスループットを出すのには限界があります。バッチプログラムではプッシュ通知に必要なデータをGaurunにキューイングするだけにしてレイテンシの大きい実際のプッシュ通知処理はすべてGaurunが行うようにしています。(Gaurunはある意味メッセージキューとジョブワーカーを内包したHTTPサーバとも言えます)そして前段に配置したnginxによるL7ロードバランサでキューイングリクエストを複数のGaurunに分散させることでプッシュ通知のスループットをさらに向上させています。GaurunのアーキテクチャGaurunは利用する側から見るとプッシュ通知に必要なAPIを提供するHTTPサーバです。curlで次のようなJSONをGaurunに送るとAPNsにプッシュ通知リクエストを送ってくれます。}'HTTPサーバの実装にはGoの標準ライブラリであるnet/httpを利用しています。net/httpを利用したサーバは素で数千〜数万req/secのスループットを叩き出せるのでGaurunのようなHTTPベースのちょっとしたAPIサーバには十分な性能が出せます。言えプッシュ通知のようにネットワークレイテンシの大きい処理を大量に並行して行うにはそれなりの工夫が必要になります。Gaurunを利用したプッシュ通知処理の流れGaurunを利用したプッシュ通知処理の流れは大まかに表現すると以下のようになります。クライアントからプッシュ通知リクエストを受け取ったGaurunはリクエストボディの内容をチャネルベースのキューにエンキューした後、クライアントにレスポンスを返します。(エンキュー自体もゴルーチンで非同期に実行されます)その後、あらかじめ起動してあるゴルーチンのワーカー達が以下のサイクルでプッシュ通知処理を実行します。pusher_maxの数だけ同時にプッシュ通知処理を実行することができます。APIを利用することで上記のアーキテクチャに変更することが可能になりました。Gaurunのパフォーマンスを最大化する続いてGaurunのパフォーマンスを最大化する方法について解説していきます。ワーカー数とプッシャー数およびキューのサイズGaurunはlistenするポート番号やGCMのAPIキー、APNsのSSL証明書のパス等の動作に必要な情報のほかにパフォーマンスに関するいくつかの設定をTOMLで記述することができます。(各設定パラメータはgaurun/CONFIGURATION.mdにまとまっています。その中でも特にGaurunのパフォーマンスに影響を与えるのがcoreセクション内の以下のパラメータです。プッシャーの最大同時起動数(0の場合は各ワーカが同期的にプッシュを実行する)各パラメータの意味はさきほどの解説で大体イメージできるのではないかと思います。プッシュ通知処理のスループットはGaurun自体の性能だけでなく、APNsやGCMとのネットワークレイテンシに大きく左右されるので状況に応じて最適な値を設定する必要があります。短い時間で大量のプッシュ通知リクエストをGaurunに送ると、当然キューのサイズが膨れていくので大量に配信することが想定される場合はあらかじめqueuesの値を大きめにしておきましょう。キューのサイズが限界に達してもチャネルへのエンキューはゴルーチンで非同期に行われるため、クライアントへのレスポンスが遅延することはありませんが、ゴルーチンがどんどん溜まっていくため、使用メモリが増えていくことになります。キューの利用状況や起動中のゴルーチンの数はGaurunが提供するモニタリングAPI(後述)で取得することができます。APNs、GCM固有のパフォーマンスチューニングGaurunではAPNsやGCMとのHTTPS接続に関するパラメータを3つ用意しています。各パラメータはAPNsとGCMで個別に設定します。1特に、大量に配信する場合にkeepalive_connsが小さいとタイムアウトが起きたり、エラーが返ってくることがあります。バルクリクエストによるキューイングの高速化GaurunのプッシュAPIはリクエストボディに複数のプッシュ通知リクエストを含めることを許しています。以下のリクエストボディだとiOSとAndroid向けに1つずつのプッシュ通知リクエストをまとめてエンキューします。]}なお、一度にエンキューできるプッシュ通知リクエストの最大数はcoreセクションのnotification_maxによって設定できます。100メルカリではバルクリクエストを利用することで全ユーザ向けに一斉プッシュ配信を実行する場合でも短い時間(10〜20分程度)でGaurunにすべてのプッシュをキューイングすることができています。不要なプッシュ通知用トークンのスクリーニングGaurunはプッシュが失敗した際にエラーログに次のようなトークンとエラー内容を含むJSONを吐くので不要なトークンをスクリーニングする際はこれを利用します。token","badge":10,"sound":"default"}メルカリではプッシュ通知用のトークンをMySQLに格納しており、プッシュ通知を行う際は必要な分をMySQLから取り出してGaurunに送ります。トークンの数は日々増えていくので期限切れ等で届かなくなったトークンを定期的に除去して、不要なプッシュ通知を行わないようにしています。プッシュのスループット制御さきほど解説したようにGaurunではプッシャーの最大同時起動数をcoreセクションのpusher_maxで設定します。/config/pushersというAPIを提供しています。このAPIを利用すれば起動中のGaurunのプッシャーの数を変更してプッシュのスループットを調整することができます。"http://127.0.0.1:1056/config/pushers?max=24"Gaurunの稼働状況を確認するGaurunは自身の稼働状況を知らせるために次のAPIを提供しています。/stat/goメルカリではこれらのAPIを利用してMackerelやKuradoでGaurunの稼働状況をモニタリングしています。@kazeburoによるMackerelプラグインもあります。/stat/appはキューの利用状況や起動しているプッシャーの数、APNsやGCMへのプッシュ通知の成功/失敗数をJSONで返します。/stat/goはgolang-stats-api-handerを利用してGoのCPU、メモリ、GCの利用状況や起動中のゴルーチン数等の値をJSONで返します。'.goroutine_num'48まとめGaurunを利用したメルカリのプッシュ配信基盤とGaurunのパフォーマンスを最大化する方法について解説しました。メルカリでは商品の購入や発送等のイベント通知に加えて内製のCRMツールによるプッシュの一斉配信を行っていますが、全ユーザ向けに配信する場合でも数台のGaurunサーバで1時間程度ですべてのプッシュ配信が完了するようにしています。値やGaurunのサーバ台数を調整することでもっと短い時間で完了することも可能ですが、あんまり早くプッシュし過ぎるとAPIサーバやDBサーバに負荷がかかってしまうのであえて台数やパラメータは抑えめにしています。Cal...2018-02-26pvpool〜メルカリの商品閲覧数カウントアップの裏側〜SREチームの@cubicdaiyaです。今回はメルカリの商品閲覧数カウ...2016-09-05メルカリのCRMツールをKaizenした話こんにちは、こんばんは。HTTP/2SREチームの@cubicdaiyaです。採用情報メルカリは常に優秀なソフトウェアエンジニアを募集しています。ご興味を持たれた方はぜひご応募ください。skills月別アーカイブスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。