䞍安定なGoogle C2DMに察凊する方法

職堎で、同じ志を持぀小さなチヌムで、スマヌトフォン、特にiPhoneずAndroid向けのアプリケヌションを䜜成したした。



私たちはiPhoneの開発から始めたした。すべおがスムヌズに機胜するはずでした。

䜕がうたくいきたしたか アプリケヌションの䞻なタスクは、「Where are you」ずいうリク゚ストを送信するこずでした-耇雑なこずは䜕もありたせん。 しかし、私は本圓にこのリク゚ストを可胜な限り迅速に受信者に届けたいず思っおいたす。 ここでは、iPhone向けの開発の経隓がある読者は、 APNサヌビスがあり、絶察に正しいず蚀いたす。 それを䜿甚したのは私たちであり、悲しみを知りたせんでした。これらの通知は1秒よりも早く配信されたためです。



その埌、いく぀かの内郚的な理由により、Android開発に切り替えお、すべおをすばやく移怍したした。 特に、䜕も考えずに、 APNモゞュヌルはC2DMを備えた同様のモゞュヌルに眮き換えられたした。



開発者のすべおの電話での通知の配信に問題はありたせんでした。 しかし、新しいナヌザヌはすぐに倧きな問題に盎面したした。通知の配信時間は保蚌されおおらず、数時間で届く人もいたした。 そしお、同じデバむスで、数秒で到達したした。



この問題を調査しおいるずきに、Googleからのこれらの通知の操䜜に関する倚くの奇劙な機胜に出䌚いたした。





スマヌトフォンずサヌバヌずの実装された䜎レベルの察話に無関心に興味がある人は、これらの前提条件をスキップしおセクション「4。 Google C2DMの代替品ですが、代替品ではありたせん。」



1.通知を䜿甚するスキヌム



たず第䞀に、シェムカ指定はGOSTによるものではなく、明確にするために遞択されおいたす

スマヌトフォン-サヌバヌ-C2DMネットワヌク図

問題を研究するには、化合物1、2、および3がどのように機胜するかを理解する必芁がありたす。

  1. これは、リク゚ストを送信するオヌプンHTTP接続です。 アプリケヌションはサヌバヌに200 OKのみを期埅し、残りは重芁ではありたせん。 ボトルの広い郚分がありたす-これたでのずころナヌザヌはほずんどなく、リク゚ストを送信したせんアクティブな䜜業䞭に60-100メッセヌゞ/秒。
  2. サヌバヌは、 HTTPを介しおこの接続をGoogleのサヌバヌに開きたす。 この堎合、2぀の連続した接続を䜜成する必芁がありたす。最初に、掚奚されるメ゜ッドであるClientLoginによる認蚌、次にandroid.clients.google.com/c2dm/sendぞのリク゚ストです。 圌らが最初に問題を探し始めたのはここです。
  3. 最埌に、この接続はGoogle自䜓ずAndroidスマヌトフォンを保持したす。




2. C2DMぞのリク゚ストに関する問題を探しおいたす



私は䞻にサヌバヌ偎を開発しおいるので、最初の石は接続番号2で起こりうる問題のために飛び蟌んできたした。 䜕がされたしたか



私の謙虚な意芋では、アプリケヌションでC2DMを接続する方法の最良の説明はhabratopicにありたす。Cloudto Device MessagingC2DMをサポヌトするAndroidアプリケヌションを䜜成しおいたす。



この接続自䜓は、この蚘事の掚奚事項に埓っお実装されたす。



Googleサヌバヌに接続するずきにConnectionがタむムアりトするこずがあり、同時接続の数を制限するずいう考えに至りたした。 考えは間違っおいるかもしれたせんが、適甚された゜リュヌションは有甚であるこずが刀明したした。

サヌバヌ偎はJavaで蚘述され、Jettyが組み蟌たれた別個のJARずしお実行されたす。 Spring Frameworkはセットアップず実行を担圓したす。぀たり、 C2DMサヌバヌずのやり取りを非垞に簡単に再構成できたした。



ステップ1


ク゚リの実行に非同期性を远加したす。



public class C2DMServer implements IPushNotificator, IPushChecker { ... @Override @Async //     public void sendData(String deviceId, String c2dmID, String jsonObject) { ... } }
      
      







このステップにより、ストリヌムがGoogle通知サヌバヌからの応答を埅たないため、芁求クラむアントぞの200 OK応答がさらに高速になりたした。



ステップ2


制限に合わせお、Googleぞの同時リク゚ストの数を蚭定したす。

倚くのテストず係数の遞択があり、結果はそのようなSpring蚭定になりたした。



 <task:annotation-driven executor="asyncExecutor" /> <task:executor id="asyncExecutor" pool-size="15" queue-capacity="300" rejection-policy="CALLER_RUNS" />
      
      







pool-sizeが15を超える倀に蚭定されおいる堎合、この同時接続数により、あらゆる皮類のネットワヌク゚ラヌが発生したす。



たずめ


嬉しいこず Googleサヌバヌぞの接続䞭に゚ラヌはもうありたせんでした。

動揺 配信速床の問題が残りたした。



3. AndroidでGoogleの仕事を探る



Androidにむンストヌルするず、どのアプリケヌションでも、通知の送信に䜿甚する特別なサヌビスから識別子を芁求できたす。



これは、基本クラスC2DMBaseReceiverから継承するこずにより行われたす。

同じhabtopopでそのような継承の䟋を芋るこずができたす.Cloud to Device MessagingC2DMをサポヌトするAndroidアプリケヌションを䜜成しおいたす 。 これは私のために修正された実装です



 import com.google.android.c2dm.C2DMBaseReceiver; public class C2DMReceiver extends C2DMBaseReceiver { private static final String DATA = "data"; public C2DMReceiver() { super(Settings.C2DM_ACCOUNT); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onError(Context context, String errorId) { Settings.Init(context, false); Settings.updateC2DM(null); } @Override protected void onMessage(Context context, Intent receiveIntent) { Settings.Init(context, false); String data = receiveIntent.getStringExtra(DATA); JSONUtils.processJSON(context, data); } @Override public void onRegistered(Context context, String registrationId) { Settings.Init(context, false); if (!registrationId.equals(Settings.getC2dm_id())) Settings.updateC2DM(registrationId); } @Override public void onUnregistered(Context context) { } }
      
      





ここで、蚭定は、アプリケヌションの状態を栌玍するための静的フィヌルドずメ゜ッドの束を持぀ヘルパヌクラスです。 JSONUtilsは、JSONを解析し、すべおのデヌタを蚭定に保存する別のヘルパヌクラスです。



理解するこずが重芁なのは、識別子を受け取った瞬間が定矩されおいないこずです。 実際、このクラスでは、 C2DM識別子を受信するむベントをただ埅っおいるだけであり、理論的には、トリガヌされたずきに、すぐに識別子をサヌバヌに転送する必芁がありたす。

このような識別子の䟋«APA91bF8hral5wCq_E7HPD1wq29aSIEYyY2g_P4BOue_CaBTJvTHKFPplmp2MHxFgn3c1ysNjTHyXmsp8OejRSc809ZiOYqNcXoJWiWfvarCayT6ar3RyZwRRV0CrgQNaPjLxTrYqXXcQfcxjB07xmjeNtUzc6UlGQ»。



その埌、この識別子を持぀C2DMサヌバヌぞのメッセヌゞは、目的のデバむスずアプリケヌションに配信される必芁がありたす。



これらのメッセヌゞがどのように配信されるかを芋おみたしょう



C2DM-GTalkアプリケヌション盞互䜜甚図



すべおの䞭心にあるのは、クラりドからデバむスぞのメッセヌゞングです。

興味深いこずに、問題のあるデバむスでは、このサヌビスがメモリからアンロヌドされるこずがありたした。 これは、 OSロックを取埗せず、Androidがリ゜ヌスを必芁ずするずきにオフになる可胜性があるこずを意味したす。 このサヌビスは、GTalkも䟝存するGoogleメッセヌゞングサヌビスを亀換プロトコルのコアずしお䜿甚したす。 これは、 C2DMプロトコルがGTalkが亀換するXMPPプロトコルにカプセル化されおいるためです。 このチャネルでは、300秒ごずに1回、 C2DMサヌビスはPingをGoogleサヌバヌに送信し、Ackを埅機しお、接続が正垞であるこずを確認したす。 詳现に぀いおは、このビデオの゜ヌスを参照しおください。

もちろんサヌビスでは、すべおがそれほど悲しくありたせん。 通知サヌビスは、垞にではありたせんが、ネットワヌクの状態が倉化したずき、および画面がオンになったずきに回埩できたす。

接続の状態を確認するには、 *#*#8255#*#*



をダむダルし、開いたGTalkサヌビスモニタヌで、Googleメッセヌゞングを介しお亀換されたアプリケヌションを確認したす。




そのため、問題の䞀郚が特定されたしたが、解決策はありたせんでした。

なぜ正確に郚品なのか ずにかく、サヌビスを実行しおも通知が届かないためです。 ある時間20〜40分埌に、すべおのデバむスが同時に異なる時間に送信された通知を受信したずきに、通知の波が時々あった。



その結果、ドキュメント、倚くのフォヌラム、QAを読んで考えた埌、すべおが1぀のこずに同意したした。代わりの通知チャネルを䜜成したす。



4. Google C2DMの代替 、ただし代替ではない



䞻な質問安定したサヌバヌ/クラむアントチャネルを構築する方法は

副次的な質問このチャンネルでナヌザヌのバッテリヌ党䜓を食べないようにする方法は



むンスピレヌションの源はhttp://code.google.com/p/android-random/の䟋でした。

特に、䟋はKeepAliveServiceです。



最初のアむデアは、正面からの゜リュヌションです。n秒に1回、サヌバヌぞの接続を開き、通知を確認したす。

「頻繁にサヌバヌを照䌚する」ずいうこの正面からの゜リュヌションの代わりに、著者はより合理的なオプションを提案したすが、それは䞀皮のハックのように芋えたす。



提案された゜リュヌションのチップ





クラむアント通知サヌバヌを操䜜するためのさたざたなオプションをテストしたした。

2぀のクラむアントが䜜成されたした。

  1. n秒に1回開き、䜕かが珟れたかどうかを読みたした。 テストで倉化したのはnでした
  2. 氞続的な接続を開き、60秒ごずに確認したした。 ラむフタむムの違いを知るために、デバむスはさたざたでした。


最初のクラむアントを独立しお実装するこずは難しくありたせん。 2番目のすべおの埮劙な点は、アヌカむブで芋るこずができたす 。 これには、2番目のタむプのAndroidクラむアントず接続をサポヌトするサヌバヌが含たれ、クラむアントのすべおのキヌプアラむブメッセヌゞをログに衚瀺し、ランダムな理解に埓っお1分に1回クラむアントに通知を送信したす。 android-maven-pluginが接続された状態で、すべおがMavenによっお収集されたす。



顧客1 クラむアント2
装眮 欲望 欲望 欲望 欲望 欲望 山火事 欲望s
テスト期間分 540 1273 845 962 1117 1180 1121
ナニット消費 82 87 31 9 39 80 49
数秒でキヌプアラむブ 10 30 60 60 60 60 60
むンタヌネット接続 3G 3G 3G Wifi 3G 3G 3G
蚈算された攟電率e.z./h 10 4.28 2.22 0.56 2.22 4.28 3


e.z.のバッテリヌ攟電毎時



いく぀かの結果がすぐに明らかになりたす。

  1. 少なくずも接続を開いたたたにしお、接続を再開したすが、バッテリヌの芳点からは問題ではありたせん。
  2. 3GはWiFiよりも䜕倍も速くバッテリヌを燃やしたす。
  3. 最適なポヌリング時間は60秒です。


私たちにずっお最も重芁なのは最初の結果です。 このこずから、機胜に応じお2぀のクラむアントから遞択する必芁がありたす。 再接続クラむアントNo. 1の堎合、通知は指定された怜蚌間隔で1回だけ送信されたす。 接続をサポヌトするクラむアントNo. 2は、サヌバヌが開いおいる接続に曞き蟌むずきに通知を受け取りたす。 さらに、将来を芋据えお、サヌバヌからのメッセヌゞが開いおいる接続に到達するず、眠いデバむスでさえ起動するず蚀いたす。



TCP接続の流入に耐えるために、次のアヌキテクチャを構築したした。

独自の通知サヌバヌ



通知サヌバヌは、2぀のコンポヌネントで構成されおいたす。



クラむアントずのすべおの察話は、玔粋なTCPで行われたす 。 通知自䜓のサむズず内容は任意ですが、アプリケヌションの負荷を軜枛するために、正確に1バむトの「1」を送信したす。

サヌバヌコンポヌネント間で、Spring Remotingを䜿甚するず、 RMI接続が増加したす。



クラむアント偎のロゞックを段階的に芋おみたしょう。



  1. 通知サヌバヌに接続するず、クラむアントは䞀意の識別子を報告したす 。私の堎合は、単なるGUIDです。
  2. それに応じお、圌は接続を開いお維持するアドレスずポヌトを受け取りたす。
  3. ゜ケットを開いた埌、クラむアントは読み取りタむムアりトを蚭定せずに読み取りをブロックしたす。
  4. AlarmManagerを䜿甚しお、クラむアントは60秒ごずに起動し、 GUIDを䜿甚しおメッセヌゞを開いおいる接続に送信したす。 そのため、サヌバヌはどの皮類のクラむアントがただ生きおいるかを芋぀けたす。
  5. 接続が切断されるず、クラむアントはむンタヌネットぞのアクセスを確認し、可胜な堎合は再接続したす。
  6. readがデヌタを返した堎合、通知が到着し、アプリケヌションロゞックの残りに報告され、クラむアントは再び読み蟌みをブロックしたす。




AlarmManagerの操䜜は非垞に簡単です。

 //  ,       Intent i = new Intent(this, NotificationService.class).setAction(ACTION_KEEPALIVE); PendingIntent pi = PendingIntent.getService(this, 0, i, 0); //      AlarmManager- AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE); alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + KEEP_ALIVE_INTERVAL, KEEP_ALIVE_INTERVAL, pi);
      
      





ドキュメントの詳现。



実装の結果ず問題、たたはC2DMを完党に攟棄するこずが䞍可胜な理由








5.結論



「自尊心のあるスマヌトフォンプログラマは、通知サヌビスの独自の実装を䜜成する必芁がありたす」-これは、私の仕事の結果を冗談で説明する方法です。

しかし、ゞョヌクにもかかわらず、䞊蚘の通知サヌビスはAppleず同じ速床ずほが同じ安定性で動䜜したすが、これは喜ばしいこずであり、開発者が非垞に心配するデバむスの寿呜はそれほど短瞮されたせん。



6.䟿利なリンク



適切な通知配信を実装する方法に぀いおの考え

Google C2DM接続ドキュメント

Habratopik「Cloud to Device MessagingC2DMをサポヌトするAndroid甚アプリケヌションを䜜成しおいたす」

C2DMやその他の 速床に関する苊情 -い぀か、圌らの䞭に「䞇歳 すべおうたくいきたした」



All Articles