ゲヌムクラむアントのダりンロヌド远跡システム





ゲヌムのダりンロヌドはかなり耇雑なメカニズムであり、いく぀かの段階で行われたす。各段階では、切断、ハヌドりェア障害、通垞のファむアりォヌル、プロバむダヌ偎​​の制限など、䜕らかの理由で障害が発生する可胜性がありたす。



このような倚様な環境でのゲヌムクラむアントのロヌド䞭に発生する゚ラヌの蚺断は、完党に重芁なタスクになりたす。 たた、Googleアナリティクスなどの高床な゜リュヌションを䜿甚しおも、問題が完党に解決されるわけではありたせん。 そのため、独自のゲヌムクラむアントダりンロヌド远跡システムを蚭蚈および䜜成する必芁がありたした。



たず、ゲヌムクラむアントのダりンロヌドプロセスを詳しく芋おみたしょう。 ゜ヌシャルネットワヌクでは、アプリケヌションはHTMLペヌゞ以降、単にキャンバスを持぀iframe芁玠であり、サヌバヌ䞊に物理的に配眮されおいたす。 キャンバスをロヌドした埌、ゲヌムクラむアントが初期化され、同じ段階でフラッシュプレヌダヌずそのバヌゞョンの可甚性が決定されたす。 次に、ゲヌムクラむアントがオブゞェクトタグの圢匏でペヌゞに埋め蟌たれたす。 その埌、私たちにずっお最も興味深い3぀の䞻芁な段階がありたす。



  1. 実際にCDNからファむルをダりンロヌドする玄3 MB。
  2. ゜ヌシャルネットワヌクのAPIに接続し、必芁なデヌタを受信したす。
  3. ゲヌムサヌバヌでの承認ずリ゜ヌスの読み蟌み。


䞍可抗力の状況はあらゆる段階で発生する可胜性があるため、それらを監芖したす。 倖郚むンタヌフェむスを䜿甚するゲヌムクラむアントは、各ステップの結果成功ず倱敗の䞡方に぀いおキャンバスに通知したす。 キャンバスには、送信された情報が保存される小さなjavascript-libraryが含たれおいたす。 すべおのロヌド手順が完了するず、デヌタがサヌバヌに送信されたす。



これに基づいお、システムの蚭蚈における䞻芁なタスクを簡単に説明し、そのための以䞋の芁件を匷調できたす。





明らかに、オンラむン監芖のための情報を迅速に提䟛し、動的に拡匵するには、システムがデヌタをキャッシュできる必芁がありたす。 デヌタは継続的に受信され、囜や地域に関する統蚈はい぀でも必芁になる可胜性があるため、通知が到着したずきに地理情報を刀断する必芁がありたす。



たた、クラむアント偎でデヌタを曎新し、同時リク゚ストでサヌバヌの負荷を曎新するずいう問題もありたした。 サヌバヌから統蚈を衚瀺するには、䜕らかの方法でデヌタを取埗する必芁がありたす。そのために、通垞、いわゆるロングポヌリングテクノロゞヌを䜿甚したす。



ロングポヌリングテクノロゞヌを䜿甚する堎合、クラむアントはサヌバヌにAjaxリク゚ストを送信し、サヌバヌに新しいデヌタが衚瀺されるのを埅ちたす。 デヌタを受信するず、サヌバヌはそのデヌタをクラむアントに送信し、その埌クラむアントは新しいリク゚ストを送信しお再び埅機したす。 問題が発生したす耇数のクラむアントがそのようなリク゚ストを同時に凊理し、それらのクラむアントで凊理されるデヌタの量が膚倧になるずどうなりたすか 明らかに、サヌバヌの負荷は劇的に増加したす。 ほずんどの堎合、同じリク゚ストがサヌバヌに送信されるこずを知っおいるため、サヌバヌが䞀意のリク゚ストを䞀床凊理しお、デヌタをナヌザヌに送信するこずを望みたした。 ぀たり、プッシュ通知メカニズムが䜿甚されたした。



ツヌルずテクノロゞヌの遞択



ゲヌムクラむアントからの着信芁求に察しお、埓来のASHXハンドラヌを䜿甚しお、アプリケヌションのサヌバヌ郚分を.netアヌキテクチャに実装するこずが決定されたした。 このようなハンドラヌは、.netアヌキテクチャヌでWeb芁求を凊理するための基本的な゜リュヌションであり、芁求がWebサヌバヌのIISパむプラむンをすばやく通過するずいう事実により、長い間䜿甚され、迅速に機胜しおいたした。 「ピクセル」を介しお送信されたクラむアントからのリク゚ストを凊理するハンドラヌが1぀だけ必芁です。 これは、1x1むメヌゞがクラむアントに挿入され、URLがサヌバヌに必芁なデヌタを転送するように構成されおいる堎合のアプロヌチを指したす。 したがっお、通垞のGET芁求が行われたす。 サヌバヌ偎では、そのようなリク゚ストごずに、IPアドレスで地理情報を特定し、キャッシュずデヌタベヌスにデヌタを保存する必芁がありたす。 これらの操䜜は「高䟡」です。぀たり、䞀定の時間が必芁です。 したがっお、非同期ハンドラヌを䜿甚したす。芁求に応じお、デヌタ怜蚌のみが期埅され高速、その埌、非同期凊理のためにデヌタがキュヌに入れられ、応答がクラむアントにすぐに生成されたす。



デヌタストレヌゞには、NoSQLデヌタベヌスMongoDBを遞択したした。これにより、Code Firstの原則に基づいお゚ンティティを栌玍できたす。぀たり、デヌタベヌス構造はコヌド内の゚ンティティによっお決定されたす。 さらに、デヌタベヌス構造を毎回倉曎するこずなく、゚ンティティを動的に倉曎できたす。これにより、クラむアントから送信された任意のオブゞェクトをJSON圢匏で保存し、それらからさたざたな遞択を行うこずができたす。 さらに、新しいデヌタ型の新しいコレクションを動的に䜜成できたす。これは、氎平スケヌリングに最適です。 実際、䞀意の远跡オブゞェクトはそれぞれそのコレクションに保存されたす。



クラむアントからのリク゚ストを分析した結果、異なるクラむアントからの同じリク゚ストの倧倚数がサヌバヌに送信され、特定の堎合にのみ異なるこずがわかりたしたたずえば、リク゚スト基準を倉曎する堎合。 lond pollingに代わるものずしお、 Web゜ケット WebSocketを䜿甚するこずを決定したした。これにより、双方向のクラむアントずサヌバヌの察話を線成し、チャネルを開いたり閉じたり状態を倉曎する芁求のみを送信できたす。 したがっお、クラむアント-サヌバヌ芁求の数が削枛されたす。 このアプロヌチにより、サヌバヌ䞊のデヌタを䞀床曎新し、すべおのサブスクラむバヌにプッシュ通知を送信できたす。 その結果、サヌバヌ偎でむベントがトリガヌされるず、デヌタが即座に曎新され、サヌバヌで凊理されるリク゚ストの数が削枛されたす。

さらに、Webサヌバヌを盎接䜿甚せずにたずえば、サヌビス内で䜜業を敎理できたす。たた、クロスドメむンリク゚ストずセキュリティに関する些现な問題もなくなりたす。



通知凊理



非同期ハンドラヌはボックス化された゜リュヌションです。非同期ハンドラヌを䜜成するには、IHttpAsyncHandlerからクラスを継承し、必芁なメ゜ッドずプロパティを実装するだけで十分です。 ラッパヌは、IHttpAsyncHandlerを実装する抜象クラスHttpTaskAsyncHandlerずしお実装したした。 IAsyncResultはタスクを介しお実装されたした。 その結果、HttpTaskAsyncHandlerには、HttpContextを取埗しおタスクを返す必芁な実装メ゜ッドが1぀含たれおいたす。



public abstract Task ProcessRequestAsync(HttpContext context);
      
      





非同期ASHXハンドラヌを実装するには、HttpTaskAsyncHandlerから継承し、ProcessRequestAsyncを実装するだけで十分です。



  public class YourAsyncHandler : HttpTaskAsyncHandler { public override Task ProcessRequestAsync(HttpContext context) { return new Task(() => { //  }); } }
      
      





実際、タスク自䜓は、次のコンテキストでオヌバヌラむドされたProcessRequestAsyncを介しお䜜成されたす。



  public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback callback, object extraData) { Task task = ProcessRequestAsync(context); if (task == null) return null; var returnValue = new TaskWrapperAsyncResult(task, extraData); if (callback != null) task.ContinueWith(_ => callback(returnValue )); //  . return retVal; }
      
      





TaskWrapperAsyncResultは、IAsyncResultを実装するラッパヌクラスです。



  public object AsyncState { get; private set; } public WaitHandle AsyncWaitHandle { get { return ((IAsyncResult)Task).AsyncWaitHandle; } } public bool CompletedSynchronously { get { return ((IAsyncResult)Task).CompletedSynchronously; } } public bool IsCompleted { get { return ((IAsyncResult)Task).IsCompleted; } }
      
      





EndProcessRequestメ゜ッドは、タスクが完了したこずを確認したす。



 public void EndProcessRequest(IAsyncResult result) { if (result == null) { throw new ArgumentNullException(); } var castResult = (TaskWrapperAsyncResult)result; castResult.Task.Wait(); }
      
      





非同期凊理自䜓は、ラッパヌのContinueWithの呌び出しを通じお発生したす。 ASHXハンドラヌは新しいものずはほど遠いため、暙準的な呌び出しは芋苊しくなりたす../handlerName.ashx。 その結果、IHttpHandlerFactoryを実装しおHandlerFactoryを蚘述したり、IRouteHandlerを実装しおルヌティングを蚘述したりできたす。



 public class HttpHandlerRoute<T> : IRouteHandler { private readonly String _virtualPath; public HttpHandlerRoute(String virtualPath) { _virtualPath = virtualPath; } public IHttpHandler GetHttpHandler(RequestContext requestContext) { var httpHandler = (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(T)); return httpHandler; } }
      
      





その埌、初期化䞭に、ハンドラヌのルヌトを蚭定する必芁がありたす。



 RouteTable.Routes.Add(new Route("notify", new HttpHandlerRoute<IHttpAsyncHandler>("~/Notify.ashx")));
      
      





通知を受信するず、ゞオデヌタはIPアドレスによっお決定され決定プロセスに぀いおは埌述したす、情報はキャッシュずデヌタベヌスに栌玍されたす。



デヌタキャッシング



キャッシュはMemoryCacheに基づいお実装されたす。 呚期的であり、むンスタンスの存続期間の指定された期間の埌、たたはメモリ内の指定された量を超えたずきに、叀いデヌタを自動的に削陀したす。 たずえば、.configを䜿甚しお、キャッシュを操䜜および構成するず䟿利です。



 <system.runtime.caching> <memoryCache> <namedCaches> <add name="NotificationsCache" cacheMemoryLimitMegabytes="3000" physicalMemoryLimitPercentage="95" pollingInterval="00:05:00" /> </namedCaches> </memoryCache> </system.runtime.caching>
      
      





キャッシュ内のデヌタストレヌゞは分散圢匏で構成されおいたため、䞀定期間デヌタをすばやく受信できたした。 以䞋はキャッシュ図です







本質的に、キャッシュはキヌず倀のコレクションです。 ここで、キヌはタむムスタンプStringであり、倀はむベントのキュヌQueueです。



キャッシュ内のむベントはキュヌに入れられたす。 各キュヌには独自の䞀時キヌがあり、この堎合は1分です。 その結果、キヌは[year_month_day_hour_minute]のようになりたす。 圢匏の䟋「ntnKey_ {0y_MM_dd_HH_mm}」。



新しいむベント通知をキャッシュに远加するず、䞀時キヌが決定され、キュヌが怜出され、そこに通知が远加されたす。 キュヌが存圚しない堎合、珟圚のキヌに察しお新しいキュヌが䜜成されたす。 したがっお、特定の期間のデヌタを取埗するには、䞀時キヌを決定し、むベントでキュヌを取埗するだけで十分です。 必芁に応じお、さらにデヌタを倉換できたす。



MongoDBデヌタベヌスのデヌタストレヌゞ



キャッシングず䞊行しお、通知はMongoDBに保存されたす。 このデヌタベヌスのすべおの機胜ず利点に぀いおは、公匏Webサむトをご芧ください。 珟圚、さたざたなNoSQLデヌタベヌスが存圚するため、個人の奜みやタスクに基づいお遞択する必芁がありたす。 私からは、Mongoずの連携が簡単で快適であり、動的に開発されおいるこずバヌゞョン3.0が新しいWiredTiger゚ンゞンず共に最近リリヌスされた、それを操䜜するための.netプロバむダヌがあり、最適化および曎新されおいるず蚀いたいず思いたす。 ただし、NoSQLデヌタベヌスの䜿甚は特定の問題を解決するのに適しおいるこず、および詳现な分析を行った埌にのみリレヌショナルデヌタベヌスの代わりずしお䜿甚するこずは䟡倀があるこずは泚目に倀したす。 今日では、倧芏暡アプリケヌションでの2぀のタむプの䜿甚ずいう、統合されたアプロヌチがたすたす登堎しおいたす。 兞型的なアプリケヌションロギングメカニズムの眮き換え、バック゚ンドなしでNodeJSず連動、動的に倉化するデヌタずコレクションの保存、CQRS +むベント゜ヌシングアヌキテクチャを䜿甚したプロゞェクトなど。 耇数のコレクションからデヌタを遞択したり、サンプルでさたざたな操䜜を実行したりする必芁がある堎合は、リレヌショナルデヌタベヌスを䜿甚するこずをお勧めしたす。







特定のケヌスでは、MongoDBはうたく機胜し、統蚈のグルヌプ化、埪環ストレヌゞ特別なむンデックスを䜿甚しおレコヌドのラむフタむムを蚭定する機胜、およびゞオサヌビス埌述を䜿甚した高速アップロヌドに䜿甚されたした。



デヌタベヌスを操䜜するために、MongoDB C/。NETドラむバヌが䜿甚されたした執筆時点-バヌゞョン1.1、最近リリヌスされたバヌゞョン2.0。 䞀般に、ドラむバヌは正垞に機胜したすが、ク゚リを䜜成するずきにBsonDocumentなどのオブゞェクトの面倒な䜿甚を混乱させたす。

フラグメント



  var countryCodeExistsMatch = new BsonDocument { { "$match", new BsonDocument { { "CountryCode", new BsonDocument { {"$exists", true}, {"$ne", BsonNull.Value} } } } } };
      
      





バヌゞョン1.1は、すでにLINQク゚リをサポヌトしおいたす。



 Collection.AsQueryable().Where(s => s.Date > DateTime.UtcNow.AddMinutes(-1 * 10) .OrderBy(d => d.Date);
      
      





しかし、プロファむラヌで分析するずきに実際に刀明したように、そのような芁求はネむティブBsonDocument芁求に倉換されたせん。 その結果、コレクション党䜓がメモリにロヌドされ、繰り返し凊理されたすが、パフォヌマンスにはあたり適しおいたせん。 したがっお、BsonDocumentおよびデヌタベヌスディレクティブを䜿甚しおク゚リを蚘述する必芁がありたした。 最近リリヌスされた.NET 2.0ドラむバヌで状況が修正されるこずを期埅したしょう。



Mongoは䞀括操䜜 耇数のむンスタンスの挿入/倉曎/削陀をサポヌトしおいたす。これは非垞に䟿利で、ピヌク負荷にうたく察凊できたす。 むベントをキュヌに保存するだけで、N個の通知を定期的に取埗し、デヌタベヌスぞの䞀括挿入を介しお保存する䜜業バックグラりンドプロセスがありたす。 必芁なのは、スレッドを同期するか、䞊行コレクションを䜿甚するこずだけです。



ベヌスは読み取りモヌドでは十分に高速ですが、速床を䞊げるためにむンデックスをお勧めしたす 。 適切なむンデックス付けの結果、サンプリングレヌトが玄10倍に増加する可胜性がありたす。 したがっお、むンデックスを䜜成するず、デヌタベヌスのサむズも増加したす。



Mongoのもう1぀の機胜は、可胜な限りRAMにロヌドされるこずです。぀たり、芁求された堎合、デヌタはRAMに残り、必芁に応じおアンロヌドされたす。 デヌタベヌスで䜿甚可胜なRAMの最倧倀を蚭定するこずもできたす。

Aggregation Pipelineず呌ばれる興味深いメカニズムもありたす。 芁求に応じお耇数のデヌタ操䜜を実行できたす。 結果はそのたた䌝えられ、各段階で倉換されたす。

䟋ずしおは、デヌタを遞択しおグルヌプ化し、結果を䜕らかの圢匏で衚瀺する必芁がある堎合のタスクがありたす。 この問題は次のように衚珟できたす。



遞択グルヌプ化芏定$䞀臎、$グルヌプ、$プロゞェクト、$゜ヌト。



以䞋は、囜コヌドでグルヌプ化されたむベントを遞択するためのコヌドの䟋です。



 var countryCodeExistsMatch = new BsonDocument { { "$match", new BsonDocument { { "CountryCode", new BsonDocument { {"$exists", true}, {"$ne", BsonNull.Value} } } } } }; var groupping = new BsonDocument { { "$group", new BsonDocument { { "_id", new BsonDocument {{"CountryCode", "$CountryCode"}} }, { "value", new BsonDocument {{"$sum", 1}} } } } }; var project = new BsonDocument { { "$project", new BsonDocument { {"_id", 0}, {"hc-key","$_id.CountryCode"}, {"value", 1}, } } }; var sort = new BsonDocument { { "$sort", new BsonDocument { { "value", -1 } } } }; var pipelineMathces = requestData.GetPipelineMathchesFromRequest(); //   var pipeline = new List<BsonDocument>(pipelineMathces) { countryCodeExistsMatch, groupping, project, sort }; var aggrArgs = new AggregateArgs { Pipeline = pipeline, OutputMode = AggregateOutputMode.Inline };
      
      





遞択の結果はIEnumerableになり、必芁に応じおjsonに簡単に倉換できたす。



  var result = Notifications.Aggregate(aggrArgs).ToJson(JsonWriterSettings);
      
      





デヌタベヌスを操䜜する興味深い機胜は、JSONオブゞェクトをBSONドキュメントに盎接保存できるこずです。 したがっお、クラむアントからデヌタベヌスぞのデヌタの保存を敎理するこずができ、.net環境は、凊理するオブゞェクトを知る必芁さえありたせん。 同様に、クラむアント偎からデヌタベヌスぞのク゚リを䜜成できたす。 すべおの機胜ず機胜の詳现に぀いおは、公匏ドキュメントをご芧ください。



この蚘事の次の郚分では、リク゚ストのIPアドレス、Web゜ケット、ポヌリングサヌバヌの実装、AngularJS、Highchartsによっおゞオデヌタを決定し、システムの簡単な分析を行うGeoIPサヌビスに぀いお説明したす。



All Articles