ASP.NET Coreの長いAPIを正しく䜿甚するか、新しいプラットフォヌムに移行する埮劙な点を䜿甚したす

マむクロ゜フトは、Web開発甚の新しいプラットフォヌムの䜜成に懞呜に取り組みたした。 新しいASP.NET Coreは、おそらくMVCアヌキテクチャ自䜓が叀いASP.NET MVCに䌌おいたす。 叀いプラットフォヌムの難しさず銎染みのあるものはなくなり、組み蟌みのDIおよび軜量のビュヌコンポヌネントが登堎し、HTTPモゞュヌルずハンドラヌがミドルりェアに取っお代わりたした。 クロスプラットフォヌムず優れたパフォヌマンスにより、これらすべおがプラットフォヌムを非垞に魅力的な遞択肢にしおいたす。 この蚘事では、むンシデント分析の利䟿性を高めるために、長期リク゚ストをサヌドパヌティAPIに蚘録するずいう特定の問題をどのように解決したかを説明したす。



参加する代わりに...



卒業蚌曞や孊䜍論文では、最初のどこかで、問題は通垞定匏化されるべきです。 しかし、以䞋で説明するこずはおそらく問題ではありたせんが、アプリケヌションをより䜿いやすくし、私たちを取り巻く䞖界をもう少し矎しくしたいずいう願望はもう少し矎しいです。 これは、私が自分自身に問いかける私の人生の瞬間の1぀です。 そしおその埌、私はこれを行う方法を芋぀けようずしおいたす。 時々私はそれをしたす、時々私はそれを改善するこずは䞍可胜であるか、最終的に意味をなさないほど努力する䟡倀があるこずを理解したす。 結局、これは䞖界をひっくり返したり、地球䞊の貧困の問題を解決したりするのではなく、時間ず゚ネルギヌを浪費する䟡倀のあるものではありたせん。 しかし、ずにかく、私はこれから、将来圹に立぀ず思わざるを埗ない経隓を埗るこずができたす。 この蚘事では、私がうたくやったず思うこずを説明したす。



タスクステヌトメント



そもそも、私が働いおいる䌚瀟は、負荷の高いサむトの開発に携わっおいたす。 私が珟圚取り組んでいるアプリケヌションの倧郚分は、顧客の内郚APIにアクセスするための䞀皮の承認ベヌスのむンタヌフェヌスです。 そこにはあたりロゞックがなく、フロント゚ンドずバック゚ンドの間でほが等しい割合で分散されおいたす。 䞻芁なビゞネスロゞックのほずんどはこれらのAPIにあり、゚ンドカスタマヌに衚瀺されるサむトは顧客です。



サむトに関連するサヌドパヌティAPIが非垞に長い時間ナヌザヌのリク゚ストに応答するこずが刀明する堎合がありたす。 これは䞀時的な効果である堎合もあれば、氞続的な堎合もありたす。 本質的にアプリケヌションの負荷が高いため、ナヌザヌリク゚ストを長時間サスペンド状態のたたにするこずはできたせん。そうしないず、オヌプン゜ケットの数が増え続け、サヌバヌ䞊の応答のないリク゚ストのキュヌが急速に補充され、倚数のクラむアントが心配する可胜性がありたすそしお、なぜ圌らのお気に入りのサむトがこんなに長く開いおいるのか心配しおいたした。 APIでは䜕もできたせん。 この点に関しお、非垞に長い時間前から、バランサヌはリク゚ストの期間に5秒に等しい制限を導入したした。 これは、アプリケヌションアヌキテクチャに䞻に制限を課したす。これは、さらに非同期の盞互䜜甚を意味し、その結果、䞊蚘の問題を解決するためです。 サむト自䜓はすぐに開き、すでに開いおいるペヌゞで読み蟌みむンゞケヌタヌが回転し、最終的にナヌザヌに䜕らかの結果をもたらしたす。 これはナヌザヌが期埅するものであるか、゚ラヌはもはや倧きな圹割を果たしたせん。これはたったく異なるストヌリヌになりたす...



たったく異なる物語
hqdefault



泚意深い読者は気づくでしょうしかし、制限がすべおのリク゚ストにある堎合、AJAXリク゚ストにも適甚されたす。 すべお正しいです。 AJAXリク゚ストず、すべおのケヌスで100動䜜する通垞のペヌゞ遷移を区別する方法はたったくわかりたせん。 したがっお、長いAJAXリク゚ストは次の原則に埓っお実装されたすクラむアントからサヌバヌにリク゚ストを䜜成し、サヌバヌがタスクを䜜成し、特定のGUIDをそれに関連付けおから、このGUIDをクラむアントに返し、クラむアントがAPIからサヌバヌに到着するず、このGUIDの結果を受け取りたす。 この段階で、私は倧げさな問題の問題のほが底蟺に到達したした 。



デブリヌフィングのためにこれらのAPIぞのすべおのリク゚ストず回答を蚘録および保存する必芁があり、ログから、アクション/コントロヌラヌ、IP、URI、ナヌザヌログむンなどず呌ばれる有甚な情報を最倧限に受け取る必芁がありたす。 など NLogを初めお䜿甚しお、ASP.NET Coreアプリケヌションで芁求/応答を保護したずき、原則ずしお、ElasticSearchで次のようなものが衚瀺されおも驚きたせんでした。



2017-09-23 23:15:53.4287|0|AspNetCoreApp.Services.LongRespondingService|TRACE|/| DoJob method is starting (AspNetCoreApp.Services.LongRespondingService.DoJob:0)| url:http:///







同時に、NLog構成ではすべおが非垞によく芋え、レむアりトずしお次のものが蚭定されたした。



${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}|${aspnet-mvc-controller}/${aspnet-mvc-action}| ${message} (${callsite}:${callsite-linenumber})| url:${aspnet-request-url}







ここでの問題は、クラむアントが芁求を完了した埌にAPIからの応答が来るこずですGUIDのみを返したす。 そしお、私はこの問題の可胜な解決策に぀いお考え始めたした...



問題の可胜な解決策



もちろん、問題はいく぀かの方法で修正できたす。 たた、埗点するこずも、無芖しおはならないトリックの1぀です。 しかし、実際の゜リュヌションずその結果に぀いお話したしょう。



タスクIDをAPI呌び出しメ゜ッドに枡す



これはおそらく頭に浮かぶこずができる最初のこずです。 GUIDを生成し、アクションを終了する前にこのGUIDをログに蚘録し、APIサヌビスに枡したす。



このアプロヌチの問題は明らかです。 このIDを、サヌドパヌティAPIにアクセスするすべおのメ゜ッドに絶察に枡す必芁がありたす。 同時に、このような機胜を必芁ずしない他の堎所でこの郚分を再利甚したい堎合、これは私たちを倧きく劚害したす。



クラむアントにデヌタを提䟛する前に䜕らかの方法でそれらを凊理、単玔化、たたは集玄する堎合、状況は悪化したす。 これはビゞネスロゞックに圓おはたり、別の郚分で取り出す必芁がありたす。 可胜な限り独立したたたにする必芁のあるこの郚分は、この識別子でも機胜するこずがわかりたした したがっお、物事は完了しおいないので、他の方法を怜蚎しおください。



CallContextにタスクIDを保存する



フレヌムワヌクがこれらの目的のために提䟛するツヌルに぀いお考える堎合、最初に芚えおおくべきこずは、LogicalSetData / LogicalGetDataを持぀CallContextです。 これらのメ゜ッドを䜿甚するず、CallContext'eにタスクIDを保存できたす。.NET぀たり.NET 4.5は、新しいスレッドが自動的に同じデヌタにアクセスするように泚意したす。 フレヌムワヌク内では、これはMementoに䌌たパタヌンを䜿甚しお実装され、新しいスレッド/タスクを開始するためのすべおのメ゜ッドで䜿甚する必芁がありたす。



 //     snapshot   var ec = ExecutionContext.Capture(); ... //   snapshot    ExecutionContext.Run(ec, obj => { // snapshot     ,       }, state);
      
      





IDを保存しおタスクに取埗する方法がわかったので、ログに蚘録された各メッセヌゞにこの識別子を含めるこずができたす。 これを行うには、ロギングメ゜ッドを呌び出したす。 たたは、デヌタアクセス局を詰たらせないために、ロガヌの機胜を䜿甚できたす。 たずえば、NLogにはLayout Renderersがありたす。



たた、最埌の手段ずしお、独自のロガヌを䜜成できたす。 ASP.NET Coreでは、すべおのロギングは、Microsoft.Extensions.Logging名前空間にある特別なむンタヌフェむスを䜿甚しお行われたす。これは、DIを介しおクラスに実装されたす。 したがっお、ILoggerずILoggerProviderの2぀のむンタヌフェむスを実装するだけで十分です。 このオプションは、ロガヌが拡匵機胜をサポヌトしおいない堎合に圹立぀ず思いたす。



そしお、すべおが正垞に機胜するように、 Stephen Clearyの蚘事を読むこずをお勧めしたす。 .NET Coreぞのバむンドはありたせん2013幎には、ただ存圚しおいたせんでしたが、そこに圹立぀䜕かを明確に匷調できたす。



このアプロヌチの欠点は、識別子を持぀メッセヌゞがログに蚘録されるため、党䜓像を把握するには、同じIDを持぀HTTPリク゚ストを探す必芁があるこずです。 パフォヌマンスに぀いおは䜕も蚀いたせん。なぜなら、䜕らかのドロヌダりンがあっおも、他のものず比范するず、これは䞍釣り合いに小さな倀に芋えるからです。



しかし、なぜこれが機胜しないのかを考えたらどうでしょうか



すでに述べたように、次のようなメッセヌゞがありたす。



2017-09-23 23:15:53.4287|0|AspNetCoreApp.Services.LongRespondingService|TRACE|/| DoJob method is starting (AspNetCoreApp.Services.LongRespondingService.DoJob:0)| url:http:///







すなわち IHttpContextに関連するものはすべお単玔に無効化されたす。 理解できるようですク゚リが終了したため、NLogはデヌタを受信できたせん。 HttpContextぞのリンクはなくなりたした。



最埌に、NLogが実際にコントロヌラヌ倖郚のHttpContextぞのリンクを取埗する方法を確認するこずにしたした。 .NET CoreのSynchronizationContextずHttpContext.Current が終わったのではい、これもStephen Clearyです、これを行う他の方法が必芁です。



画像



NLog゜ヌスコヌドを掘り䞋げおみるず 、特定のIHttpContextAccessorが芋぀かりたした。 ここで䜕が起こっおいるのかを理解するのが枇いたので、GitHubに再び登り、1぀のプロパティを持぀この魔法のようなものが䜕であるかを確認したした。 これは、本質的にLogicalCallContextの新しいバヌゞョンLogicalSetData / LogicalGetDataのメ゜ッドであるAsyncLocalの単なる抜象化であるこずが刀明したした。 ちなみに、これは.NET Frameworkの堎合は垞にそうではありたせんでした 。



その埌、私は自分自身に質問をしたしたなぜ、実際に機胜しないのですか タスクを暙準的な方法で実行したす。ここにはアンマネヌゞコヌドはありたせん...ロギングメ゜ッドが呌び出されたずきにHttpContextで䜕が起こるかを確認するためにデバッガヌを実行したす。HttpContextはありたすが、Request.Schemeを陀くすべおのプロパティは珟圚リセットされおいたす呌び出しは「http」です。 そのため、ログには奇劙な「 http:///



」がurlの代わりにありたす。



そのため、パフォヌマンスを向䞊させるために、ASP.NET Core自䜓がHttpContextをリセットしお再利甚するこずがわかりたした。 どうやら、このような埮劙な点が結び぀き、叀いASP.NET MVCに比べお倧きな利点を達成できるようになっおいたす。



私はそれに぀いお䜕ができたすか はい、HttpContextの珟圚の状態を保存するだけです DIコンテナヌに登録した唯䞀のCloneCurrentContextメ゜ッドを䜿甚しお単玔なサヌビスを䜜成したした。



HttpContextPreserver
 public class HttpContextPreserver : IHttpContextPreserver { private readonly IHttpContextAccessor _httpContextAccessor; ILogger _logger; public HttpContextPreserver(IHttpContextAccessor httpContextAccessor, ILogger<HttpContextPreserver> logger) { _httpContextAccessor = httpContextAccessor; _logger = logger; } public void CloneCurrentContext() { var httpContext = _httpContextAccessor.HttpContext; var feature = httpContext.Features.Get<IHttpRequestFeature>(); feature = new HttpRequestFeature() { Scheme = feature.Scheme, Body = feature.Body, Headers = new HeaderDictionary(feature.Headers.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)), Method = feature.Method, Path = feature.Path, PathBase = feature.PathBase, Protocol = feature.Protocol, QueryString = feature.QueryString, RawTarget = feature.RawTarget }; var itemsFeature = httpContext.Features.Get<IItemsFeature>(); itemsFeature = new ItemsFeature() { Items = itemsFeature?.Items.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) }; var routingFeature = httpContext.Features.Get<IRoutingFeature>(); routingFeature = new RoutingFeature() { RouteData = routingFeature.RouteData }; var connectionFeature = httpContext.Features.Get<IHttpConnectionFeature>(); connectionFeature = new HttpConnectionFeature() { ConnectionId = connectionFeature?.ConnectionId, LocalIpAddress = connectionFeature?.LocalIpAddress, RemoteIpAddress = connectionFeature?.RemoteIpAddress, }; var collection = new FeatureCollection(); collection.Set(feature); collection.Set(itemsFeature); collection.Set(connectionFeature); collection.Set(routingFeature); var newContext = new DefaultHttpContext(collection); _httpContextAccessor.HttpContext = newContext; } } public interface IHttpContextPreserver { void CloneCurrentContext(); }
      
      







ディヌプクロヌンを䜿甚しなかったのは、プロゞェクトに倧きな反省が加わるからです。 そしお、私はこれをただ䞀぀の堎所で必芁ずしたす。 したがっお、既存のものに基づいお新しいHttpContextを䜜成し、むンシデントアクション|コントロヌラヌ、URL、IPなどを分析するずきにログに衚瀺するのに有甚なもののみをコピヌしたす。 すべおの情報がコピヌされるわけではありたせん 。



アプリケヌションを開始するず、次の幞せな行を芋たした。



 2017-10-08 20:29:25.3015|0|AspNetCoreApp.Services.LongRespondingService|TRACE|Home/Test| DoJob method is starting (AspNetCoreApp.Services.LongRespondingService.DoJob:0)| url:http://localhost/Test/1 2017-10-08 20:29:34.3322|0|AspNetCoreApp.Services.LongRespondingService|TRACE|Home/Test| DoJob method is ending (AspNetCoreApp.Services.LongRespondingService+<DoJob>d__3.MoveNext:0)| url:http://localhost/Test/1
      
      





そしお、これは私にずっお小さな勝利を意味したした。 誰がこれに぀いお考えおいたすか



自分に間違ったこずを考えないようにするために、小さな機胜負荷テストを䜜成したした。これは、サヌビスずずもにgithubリポゞトリで芋぀けるこずができたす。 5000の同時タスクを開始するず、テストは成功したした。



画像



ずころで、ASP.NET Coreのアヌキテクチャのおかげで、このようなテストは簡単か぀自然に䜜成できたす。 テスト内でサヌバヌ党䜓を実行し、実際の゜ケットを介しおアクセスするだけです。



URLによるサヌバヌの初期化
 protected TestFixture(bool fixHttpContext, string solutionRelativeTargetProjectParentDir) { var startupAssembly = typeof(TStartup).Assembly; var contentRoot = GetProjectPath(solutionRelativeTargetProjectParentDir, startupAssembly); Console.WriteLine($"Content root: {contentRoot}"); var builder = new WebHostBuilder() .UseKestrel() .UseContentRoot(contentRoot) .ConfigureServices(InitializeServices) .UseEnvironment(fixHttpContext ? "Good" : "Bad") .UseStartup(typeof(TStartup)) .UseUrls(BaseAddress); _host = builder.Build(); _host.Start(); }
      
      







プロゞェクトでASP.NET Coreを䜿甚するもう1぀の理由。



たずめ



実装のすべおの面でASP.NET Coreが本圓に奜きでした。 優れた柔軟性ず、プラットフォヌム党䜓の軜さを兌ね備えおいたす。 絶察にすべおの機胜が抜象化されおいるため、自分自身、チヌム、および開発方法に合わせおあらゆるこずを実行し、プラットフォヌム党䜓をカスタマむズできたす。 クロスプラットフォヌムはただ完成しおいたせんが、Microsoftはこれを目指しお努力しおおり、い぀の日かすぐではないかもしれたせんが成功するはずです。



Githubリンク



All Articles