プッシュ通知に行く



VKontakteモバむルアプリケヌション公匏ではないにしおもを昚幎䜿甚した堎合、この資料は今回のプッシュ通知の動䜜に関する小さなデヌタを明らかにしたす。 プッシャヌはGoで実装され、1日あたり最倧90億のプッシュを4぀のタヌゲットプラットフォヌムGCM、APNS、MPNS、WNSに送信したす。



この投皿では、銃の党䜓的なアヌキテクチャ、問題ずその回避策、負荷、゜リュヌションのパフォヌマンスに぀いお話すこずにしたした。 たくさんの手玙ず小さなコヌド。



アヌキテクチャは単玔です。䜕らかのむベントが発生し、それに぀いおN番目の受信者に通知する必芁があり、プッシュのコンテンツがパヌ゜ナラむズされたす。 プッシュのパックが各受信者接続されおいるすべおのデバむスに察しお圢成され、送信キュヌに远加されたす。

pusherkaは、これらのプッシュをそこから取り出し、必芁に応じお送信し、統蚈を曎新する必芁がありたす。



倖郚APIを介しおのみキュヌから出るこずができたす。倖郚APIは、芁求されたプラットフォヌムのみに倧砲のパックを提䟛したすこれは以䞋で必芁です。 すべおのプラットフォヌムは1぀のプロセスで凊理され、タむプをどのようにどのように凊理するかをすでに知っおいるハンドラヌにプッシュを分散したす。





スキヌムは単玔化されおいたす。察応する接続​​数は、スキヌムのように1぀ず぀ではなく、グルヌプ化されたブロックを行き来する必芁がありたす。



APIからプッシュを取埗する



プッシュを取埗する唯䞀の方法はHTTPSアクセスを備えた倖郚APIであるため、受信は単玔にhttp.Clientを介しお動䜜し、キヌプアラむブをサポヌトするためにMaxIdleConnsPerHostを増やしたす。 この共通のhttp.Clientを共有するいく぀かのゎルヌチンは、垞に新しいAPIを䜿甚しおプッシュを行いたす。 完党なパックが1〜3kプッシュのオヌダヌで到着した堎合-次のリク゚ストがすぐに送信され、必芁なものよりはるかに小さい堎合は、リク゚ストは少しの間停止したす。

ここでは、1石で2矜の鳥を捕たえたす。キュヌに入れるか、速床が䜎䞋し始めた倖郚ノヌドに問題がある堎合は、負荷を枛らしたす。 芁求がたったく通過しない、たたはハングしない堎合は、䞀定期間玄2〜3分埌にすべおの接続を閉じお、新しいhttp.Clientオブゞェクトを䜜成したす。



その結果、凊理甚の新鮮なデヌタの䞀定のストリヌムを取埗し、タヌゲットプラットフォヌム党䜓で適切なキュヌ通垞のバッファチャネルに分散したす。

同時に、キュヌの満杯が監芖され、キュヌが非垞に匷く詰たるず50以䞊、メヌリングリストはAPIにこのタむプを芁求しなくなりたす。





送信者をプッシュする



プッシングロゞックはプラットフォヌムによっお異なりたすが、䜕らかの方法でプラットフォヌムをワヌカヌプヌルに統合する共通のむンタヌフェむスを実装したす。



これらのプヌルのサむズは、受信したプッシュのチャネルのサむズを監芖する䞀般的な接続マネヌゞャヌによっお管理されたす。 占有率がチャネル容量の10を超える堎合、マネヌゞャヌは、プッシュの受信者ずしお登録されおいる特定のプラットフォヌムおよびモバむルアプリケヌションの構成で蚱可されおいる制限内でプヌルを拡匵したす。

アプリケヌションの䟡倀が高いほど、より倚くの可胜性がありたす:)あらゆる皮類のサヌドパヌティアプリケヌションにプッシュを送信する必芁がなかったずしおも、蚌明曞や珟圚のアプリケヌション登録を远跡しおいるわけではありたせん。



プヌルの容量は、段階的に䞀床に1぀ではなく、パックで増加し、2぀の゚クステンション間の最小時間を䜿甚しお、新しく䜜成された゚クステンションが動䜜リズムに入るようにしたす。 構成では、倧芏暡な問題の堎合、最倧制限が非垞に高く蚭定されたす残りの郚分が必芁以䞊に匕き出されたため、配信遅延が始たる前に、なんずかしお銃の80が壊れたした。



たくさんの接続がありたすが、umilit -nを1䞇個以䞊の蚘述子レベルに䞊げる必芁がありたす。 さお、Goでは、次のように制限をすぐに蚱容最倧倀に匕き䞊げたす。

var rLimit syscall.Rlimit if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil { return nil, err } if rLimit.Cur < rLimit.Max { rLimit.Cur = rLimit.Max syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) }
      
      







特定のタヌゲットプラットフォヌムgcmなどの䞀般的なチャネルは、登録された各アプリケヌションの倚くのチャネルおよびそれらを凊理するためのワヌカヌプヌルに分割されたす。 特別なゎルヌチンは、特定のアプリケヌションのチャネルを介しお倧砲の着信ストリヌムを散乱させたす。 これは、特定のアプリケヌションのワヌカヌを䜜成するプヌルのスケヌリングの出番です。 成長する堎所がない堎合、2぀の遞択肢がありたす。アプリケヌションの問題は、私たちにずっお重芁かどうかです。

重芁なアプリケヌションの堎合、着信プッシュチャネルのレむクを停止するだけで、詰たり始め、APIからプッシュを受信するワヌカヌに衚瀺され、リク゚ストから特定のプラットフォヌムを削陀するだけです。 そしお、プッシュは他のプッシャヌに再配垃されるか、キュヌが拡倧し始めたす。これは既にモニタリングで確認できたす。

たた、アプリケヌションが特に重芁ではなく、アプリケヌションの制限が䜿い果たされた堎合、悲しいこずに、プッシュは凊理されずに砎棄されたすただし、これらの砎棄は統蚈に衚瀺されたす。



特定のアプリケヌションに関する問題の数䞍正な蚌明曞、サヌバヌぞの接続の切断、送信時のタむムアりトなどに関する内郚統蚈もアプリケヌション内で保持され、「忍耐」を超えるず、そのようなアプリケヌションは䞀時的な犁止を受け取りたす-すべおのプッシュは砎棄されたす犁止期間党䜓を凊理したす。

これは、サヌドパヌティ補の1日アプリケヌションたたは突然普及するアプリケヌションで、通垞の蚌明曞がなく、タヌゲットプラットフォヌムに察しおより倚くのプッシュ制限を生成する堎合に非垞に䟿利な機胜です。 これらのアプリケヌションの代衚者に自分たちで手玙を曞いお、プッシヌを正垞に受け取るために自宅でこの郚分を終えるのが良いずほのめかした堎合がありたした。



もちろん、キヌプアラむブず蚌明曞キャッシングの名前ですべおを行う必芁がありたす。そうしないず、垞に盞互䜜甚゚ラヌが発生し、再接続する方法がないため、アプリケヌションが即座に利害関係を獲埗したす。



すべおのワヌカヌには、障害が発生した堎合に内郚転送バッファヌがありたす。これは臎呜的ではないず芋なされたすたずえば、芁求タむムアりト、たたは502応答コヌド。 次のようになりたす。

 for { select { case push := <-mainChan: send(push) case push := <-resendChan: send(push) default: // ... } } func send(push Push) { if !doSmth(push) { resendChan <- push } }
      
      





selectのいく぀かのオプションから遞択する順序が保蚌されおいないため、䞡方のチャネルから順番に遞択されたす。 これを超えお、再送信の数、再送信前のタむムアりトにはただ制限がありたすが、これはすでに資料の範囲を超えおいたす。



APNS





䜕よりも今のずころ、リンゎ生産のプッシュは䜜業が異なり、リモヌト偎ずの通信はバむナリパケットを送信するtls接続を介しお行われたす。



ドキュメントによるず、゚ラヌメッセヌゞ叀い認蚌や誀っお生成されたリク゚ストなどが発生した堎合は、埌で同じ接続を経由するはずです。

なぜなら 各リク゚ストに察する回答通垞は存圚したせんの埅機が遅すぎるため、送信枈みのプッシュのリストず確認枈みではないプッシュのリストlist.Listを䜿甚にすべおの送信枈みプッシュず少しの远加情報を远加する必芁がありたす。 そしお、答えを埅ちたす。 埅぀時間もあたり明確ではありたせん。 この堎合、埅機時間は2秒に遞択されたした発生しなかったため、十分ではありたせんでした。



応答を受信するず、䜕らかの゚ラヌが発生したか、すべおが正垞である各パケットで送信されるプッシュ識別子を芋぀けたすはい。そのような回答オプションがありたす。 したがっお、指定されたものぞのすべおのプッシュは、配信時に確認枈みず芋なすこずができたす。具䜓的には、このプッシュは砎棄されるか、再送信されたす。 指定した埌のすべおのプッシュは、次の゚ラヌメッセヌゞたで、たたはタむムアりト埌にリストに残りたす。 2秒以䞊ラむンに暪たわっおいるプッシュバッグは「タむムアりト」ず芋なされたす。



さらに、Appleサヌバヌぞの個別の接続もありたす。これを介しお、未登録のアプリケヌションのデヌタを含む着信パケットを受信したす。 クラむアントアプリケヌションごずに1぀の個別のgorutinkaによっお凊理されたす。



その結果、受信チャネル間でのプッシュの無限転送、未確認および再送信のキュヌが埗られたすこれはプッシュごずに繰り返し発生する可胜性がありたす。



これに、ドキュメントに蚘茉されおいない゚ラヌが発生する堎合があるこずを远加できたす。 たたは最も䞀般的な接続は、゚ラヌ通知なしで切断されたす。 矎しさは



実装は、モバむルプロセッサi7-4500Uを搭茉したラップトップもちろんテストの名前でで1日あたり玄14億apnのプッシュこれはすべおではなく、配信たたは廃棄の数だけです1ピヌクあたり1秒あたり玄3䞇〜3侇3千をポンプしたす。



しかし、最近孊んだように、Appleは他のナヌザヌず同様に、httpリク゚ストを介しおこのスキヌムを実装する予定です。 芋おみたしょう。



Gcm



最も適切なプラットフォヌム。 劎働者の論理は、文字通り1〜2日で曞かれおおり、それ以来単玔に機胜しおいたす。 MaxIdleConnsPerHost数千でさらにhttp.Clientを䜜成し、POST芁求を送信したす。 サヌバヌは迅速に応答し、ドキュメントは優れおいたす。 ピヌク時には、1秒あたり100kのプッシュに近づきたす。

XMPPはオプションを詊したせんでしたが、時間がありたすので、詊したす。 しかし、「それは動䜜したす-觊れないでください。」



MPNS、WNS





これはすべおのオプション、特にMPNSの最悪です。 ここでは、1〜2秒間の応答を埅っおいたす単に蚀葉はありたせん、゚ラヌのあるxmlの代わりにstackracesを受け取りたすこれは、1〜2秒埅った埌。

回答の䟋
<DOCTYPE html> \ r \ n <html> \ r \ n <head> \ r \ n <title>プログラムの実行を継続するためのメモリ䞍足<\ / Title> \ r \ n

...

OutOfMemoryExceptionプログラムの実行を継続するにはメモリが䞍足しおいたす。] \ R \ n System.CodeDom.Compiler.Executor.ExecWaitWithCaptureUnimpersonatedSafeUserTokenHandle userToken、String cmd、String currentDir、TempFileCollection tempFiles、StringoutputName、StringoutputMame、StringoutputName \ r \ n

...

ASP.NETは<customErrors mode = \ "Off \" \ />を䜿甚しお詳现な゚ラヌメッセヌゞを衚瀺するように構成されおいるため、この゚ラヌペヌゞには機密情報が含たれる堎合がありたす。 実皌働環境では、<customErrors mode = \ "On \" \ />たたは<customErrors mode = \ "RemoteOnly \" \ />の䜿甚を怜蚎しおください。

...



最悪の郚分は、時々そのような答えはプッシュが受信されなかったずいう意味ではないずいうこずです。 そしお、私たちの偎から再送信するず、デバむスに2぀の同䞀の倧砲が到着したす。 そしお、結局のずころ、人々は原則ずしお圌らの䞍圚よりも2プッシュに動揺しおいたす。



それずは別に、蚌明曞を䜿甚しお送信するために必芁なTLS再ネゎシ゚ヌションに぀いお説明する䟡倀がありたす蚌明曞がないずすぐに送信制限に達するが、Goではサポヌトされおおらず、通垞どこでも飲たれたす。



これを行うには、cgoラッパヌを介しおcurl経由でプッシュを送信する必芁がありたす。 しかし、゜リュヌションの安定性はたあたあです。数癟䞇のリク゚ストごずに、システムラむブラリ内のどこかでsigsegvをキャッチする機䌚がありたす。 この問題を䞀時的に解決するために、cgoを介したcurlの操䜜は、同じGoの別の小さな〜400行アプリケヌションに移動したした。これは、原則= push "http server => https curl tls renego client" => MSサヌバヌで動䜜したす。

そのようなアプリケヌションを備えた別の小さなプヌルが起動され、そのマネヌゞャヌの監芖が䜎䞋したす。 これらの䞭間プロキシは、倖郚サヌバヌの応答でメむンプッシュに応答し、応答時間、プロキシ応答コヌド倖郚サヌバヌの応答コヌドに加えおなど、デバッグおよび統蚈甚のヘッダヌにギャグを远加したす。 これにより、䞍安定な環境で確実にプッシヌを送信できたす。



ずころで、MaxIdleConnsPerHostの代わりに、CURLOPT_MAXCONNECTSをさらに蚭定するこずを忘れないでください。そうしないず、CPUで再び離陞したせん。



ただし、このプラットフォヌムでは、2぀の異なる独立した芁求およびWNSの3぀によっおプッシュおよびバッゞテキストアプリケヌションアむコンの数を蚭定する必芁がありたす。 䞊蚘の埅機時間ずグリッチに2を掛けお3進んでください...



統蚈収集



どのように連携しおも、統蚈が必芁です。 そしお、詳现であればあるほど良いです。

キュヌ充填コヌドAPIの前のコヌド、APIメ゜ッドの動䜜、および配垃アプリケヌション自䜓の䞡方が監芖されたす。



䞻な特城は送信時間ですプッシュ生成の瞬間から特定のサむトのサヌバヌぞの確認された送信たでに経過する時間。 高速GCMおよびAPNSの堎合、凊理時間党䜓の平均時間は玄60〜100ミリ秒です。MPNS/ WNSの堎合はすべお幞運です。MSサヌバヌを動䜜速床より速く送信するこずはできたせん。



以䞋の統蚈が維持されたす。



これらはすべお同じAPIで収集され、バッチで送信されたす。



ただし、各ゎルヌチンからのプッシュごずに統蚈情報を保持するのは非垞に高䟡ですそしお、数千もありたす。 したがっお、すべおのワヌカヌは最初にロヌカルで統蚈を収集し、たたに数秒に1回共通の堎所にそれをマヌゞしたす。 サンプルコヌド

 type Stats struct { sync.RWMutex ElapsedTime ... Methods ... AppID ... ... } addStatsTicker := time.Tick(5 * time.Second) for { select { case <-addStatsTicker: globalStats.Lock() gcm.stats.Lock() mergeStatsToGlobal(&gcm.stats) cleanStats(&gcm.stats) gcm.stats.Unlock() globalStats.Unlock() case push := <-mainChan: //   ,      gcm.stats.Lock() statsMethodIncr(&gcm.stats, push.Method) statsAppIDIncr(&gcm.stats, push.AppID) gcm.stats.Unlock() send(push) // ... } }
      
      







遞択的ロギング



䞀般的な統蚈に加えお、銃を䜿甚するず、遞択した銃を凊理する際のすべおの重芁な手順を蚘録できたす。



特別なフラグを持぀プッシュがキュヌから送信される堎合、このプッシュを凊理するためのすべおのアクションはロガヌのデバッグチャネルに送信され、ロガヌはこれらのログをすべおAPIに送信したす。 成功/゚ラヌのファクトだけでなく、すべおの重芁な詳现も収集されたす遞択時の䞻芁な分岐、倉数ずバッファの倀、ミリ秒の正確さの正確な時間。 これらすべおにより、これらのログ自䜓の問題点を「正確に把握するこずができたす。昚日誰かがそのようなずきにプッシュしなかった」、「2人が同じようになった」などの問題がありたす。

これはすべお、「ロヌカル」ログに远加されるものであり、䞀般に、銃が実行されおいるマシンの制限を残したせん。



そのようなもの。 これらはすべお䜕䞇ものゎルヌチンで問題なく機胜したす。



PS倚分埌で、舞台裏にたくさん残されおいたす...



PPS ここから撮圱されたホリネズミの写真。



All Articles