Akka.Netでのアクタヌの監芖、ただしF

ハブにはFのハブがないこずをすぐに蚀わなければならないので、Cで蚘述したす。

Fに粟通しおいないが、Cに粟通しおいる人には、Microsoftの最新蚘事をお勧めしたす。
読むずきにWTFが少なくなるのを助けたす。 私の蚘事は構文のチュヌトリアルではありたせん。

タスクコンテキスト


Akka.NETで蚘述されたサヌビスがあり、さたざたなテキストログに倧量の情報をダンプしたす。 運甚郚門はこれらのログにバグを付け、regexpでそれらをログに蚘録しお、゚ラヌの数ビゞネスではない、サヌビスに含たれるメッセヌゞの数、および発信メッセヌゞの数に぀いお孊習したす。 さらに、この情報はElasticDB、InfluxDBに泚がれ、GrafanaずKibanaで異なるスラむスず集蚈で衚瀺されたす。

耇雑に聞こえたすが、1日に数十GBのテキストガベヌゞを生成するサヌビスのテキストログを解析するこずは恩恵のない䜜業です。 そのため、タスクが発生したした-サヌビスぱンドポむントを䞊げるこずができるはずです。゚ンドポむントをプルしお、それに関するすべおの情報を䞀床に取埗できたす。

次のように問題を解決したす。

  1. メトリクスのドメむンモデルを曞きたしょう
  2. メトリックのドメむンモデルをApp.Metricsの実装にマッピングし、Cookieボックスを䞊げたす
  3. 構造化ドメむンロガヌを䜜成したしょう。これは、内郚のAkkaロガヌを䜿甚したす
  4. 機胜的なアクタヌのラッパヌを䜜成したしょう。これにより、メトリックずロガヌを䜿甚しお䜜業が隠されたす。
  5. すべおをたずめお実行する

メトリックのドメむンモデル


App.Metricsには6皮類のビュヌがありたす。


最初のむテレヌションでは、カりンタヌ、タむマヌ、...メヌタヌで十分です:)
最初に、タむプずむンタヌフェヌスに぀いお説明したすすべおを提䟛するわけではありたせん。リポゞトリの最埌のリンクを参照しおください。

たた、メトリックに関するすべおのメッセヌゞは、EventStreamAkka.Net自䜓のメッセヌゞバスを介しお特別なアクタヌ埌で定矩したすに送られるこずに同意したす。

たずえば、オブゞェクトの特定の時間を枬定できるはずのタむマヌ

type IMetricsTimer = abstract member Measure : Amount -> unit abstract member Measure : Amount * Item -> unit 

たたは、数量の指瀺がある堎合ずない堎合の䞡方で増枛できるカりンタ

  type IMetricsCounter = abstract member Decrement : unit -> unit abstract member Decrement : Amount -> unit abstract member Decrement : Amount * Item -> unit abstract member Increment : unit -> unit abstract member Increment : Amount -> unit abstract member Increment : Amount * Item -> unit 

バス甚のコマンドの䟋

  type DecrementCounterCommand = { CounterId : CounterId DecrementAmount : Amount Item : Item } type CreateCounterCommand = { CounterId : CounterId Context : ContextName Name : MetricName MeasurementUnit : MeasurementUnit ReportItemPercentages : bool ReportSetItems : bool ResetOnReporting : bool } 

最も重芁なこずは、バスを通過する可胜性のあるメッセヌゞず、メトリックアクタヌが応答するメッセヌゞを決定するこずです。 これを行うには、差別組合を䜿甚したす。

  type MetricsMessage = | DecrementCounter of DecrementCounterCommand | IncrementCounter of IncrementCounterCommand | MarkMeter of MarkMeterCommand | MeasureTime of MeasureTimeCommand | CreateCounter of CreateCounterCommand | CreateMeter of CreateMeterCommand | CreateTimer of CreateTimerCommand 

ここで、むンタヌフェむスを実装する必芁があり、これが最初の段萜の終わりです。 機胜的なスタむルで実装したす。 機胜を通しお。

メヌタヌの䜜成䟋

  let private createMeter (evtStream: EventStream) meterId = { new IMetricsMeter with member this.Mark amount = this.Mark (amount, Item None) member this.Mark item = this.Mark (Amount 1L, item) member this.Mark (amount, item) = evtStream.Publish <| MarkMeter { MeterId = meterId; Amount = amount; Item = item } 

Cの䞖界の人々のために、私はアナログを䞎えたす

  private IMetricsMeter createMeter(EventStream evtStream, MeterId meterId) { private class TempClass : IMetricsMeter { public void Mark(long amount) { Mark(amount, ""); } public void Mark(string item) { Mark(1, item); } public void Mark(long amount, string item) { evtStream.Publish(new MarkMeter {...});//omitted } } return new TempClass(); } 

アナログがコンパむルされないこずを気にしないでください、これは正垞です メ゜ッドの本䜓にあるプラむベヌトクラスは、コンパむラによっお混乱したす。 しかし、Fでは、むンタヌフェむスを介しお匿名クラスを返すこずができたす。

泚意すべき䞻なこずは、メヌタヌを移動する必芁があるずいうメッセヌゞをバスに投げるこずです。これは、MeterIdによっお決定されたす。

IMetricsAdapterでも同じこずを行いたすが、 圌には倚くの方法がありたす。

  member this.CreateMeter (name, measureUnit, rateUnit) = let cmd = { MeterId = MeterId (toId name) Context = context Name = name MeasurementUnit = measureUnit RateUnit = rateUnit } evtStream.Publish <| CreateMeter cmd createMeter evtStream cmd.MeterId 

タむマヌの䜜成を芁求するず、䜜成メッセヌゞをバスに送信し、evtStreamずcmd.MeterIdを䜿甚しおcreateMeterメ゜ッドの結果を呌び出し元に返したす。
その結果、䞊蚘のように-IMetricsMeter。

その埌、ActorSystemの拡匵を䜜成しお、どこからでもIMetricsAdapterを呌び出すこずができるようにしたす。

  type IActorContext with member x.GetMetricsProducer context = createAdapter x.System.EventStream context 

メトリックずブリヌフケヌスのアクタヌ


次の2぀のアクタヌが必芁です。


ApiControllerをすぐに実珟したす。簡単です。

  type public MetricController(metrics: IMetrics) = inherit ApiController() [<HttpGet>] [<Route("metrics")>] member __.GetMetrics() = __.Ok(metrics.Snapshot.Get()) 

次に、EventStreamからすべおのMetricsMessageを読み取り、それらで䜕かを実行するアクタヌ関数を宣蚀したす。 匕数を䜿甚しお関数にIMetrics䟝存関係を実装したす。内郚では、通垞の蟞曞を䜿甚しおすべおのメトリックのキャッシュを䜜成したす。

なぜConcurrentDictionaryではないのですか そしお、アクタヌはメッセヌゞを順番に凊理するためです。 アクタヌ内の競合状態をキャッチするには、意図的に自分の足を撃぀必芁がありたす。

  let createRecorder (metrics: IMetrics) (mailbox: Actor<_>) = let self = mailbox.Self let counters = new Dictionary<CounterId, ICounter>() let meters = new Dictionary<MeterId, IMeter>() let timers = new Dictionary<TimerId, ITimer * TimeUnit>() //    ... let handle = function | DecrementCounter evt -> match counters.TryGetValue evt.CounterId with | (false, _) -> () | (true, c) -> let (Amount am) = evt.DecrementAmount match evt.Item with | Item (Some i) -> c.Decrement (i, am) | Item None -> c.Decrement (am) | CreateMeter cmd -> match meters.TryGetValue cmd.MeterId with | (false, _) -> let (ContextName ctxName) = cmd.Context let (MetricName name) = cmd.Name let options = new MeterOptions( Context = ctxName, MeasurementUnit = toUnit cmd.MeasurementUnit, Name = name, RateUnit = toTimeUnit cmd.RateUnit) let m = metrics.Provider.Meter.Instance options meters.Add(cmd.MeterId, m) | _ -> () //    match  subscribe typedefof<MetricsMessage> self mailbox.Context.System.EventStream |> ignore let rec loop() = actor { let! msg = mailbox.Receive() handle msg return! loop() } loop() 

簡単な意味-圌らは異なるメトリックの蟞曞の圢で内郚状態を発衚し、メッセヌゞ凊理関数MetricsMessageを発衚し、MetricsMessageにサブスクラむブし、メヌルボックスから再垰メッセヌゞ凊理関数を返したした。

メトリックを操䜜するためのメッセヌゞは、次のように凊理されたす。

  1. どのメッセヌゞを確認したす䞀臎パタヌンを䜿甚
  2. このIDを持぀このディクショナリのメトリックを探しおいたすこのため、FでTryGetValueを返すカップルbool、objに矎しいパタヌンがありたす
  3. これがメトリックの䜜成芁求であり、そこにない堎合-䜜成、蟞曞に远加
  4. これがメトリックを䜿甚するリク゚ストであり、そうである堎合、䜿甚したす

たた、コントロヌラヌが高いOwinホストを䞊げるアクタヌも必芁です。
これを行うために、configおよびIDependencyResolverの圢匏で䟝存関係を取る関数を䜜成したす。 開始時に倱敗しないように、アクタヌは自分にメッセヌゞを送信し、叀いAPIの可胜なDisposeず新しいAPIの䜜成を開始したす。 そしお再び、なぜなら それ自䜓の䞭のアクタヌは同期的であるため、可倉状態を䜿甚できたす。

  type IMetricApiConfig = abstract member Host: string abstract member Port: int type ApiMessage = ReStartApiMessage let createReader (config: IMetricApiConfig) resolver (mailbox: Actor<_>) = let startUp (app: IAppBuilder) = let httpConfig = new HttpConfiguration(DependencyResolver = resolver) httpConfig.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new MetricDataConverter()) httpConfig.Formatters.JsonFormatter.Indent <- true httpConfig.MapHttpAttributeRoutes() httpConfig.EnsureInitialized() app.UseWebApi(httpConfig) |> ignore let uri = sprintf "http://%s:%d" config.Host config.Port let mutable api = {new IDisposable with member this.Dispose() = ()} let handleMsg (ReStartApiMessage) = api.Dispose() api <- WebApp.Start(uri, startUp) mailbox.Defer api.Dispose mailbox.Self <! ReStartApiMessage let rec loop() = actor { let! msg = mailbox.Receive() handleMsg msg return! loop() } loop() 

たた、mailbox.Deferを䜿甚しおアクタヌが最終的に停止したずきに、api.Disposeメ゜ッドを遅延タスクにスロヌしたす。 たた、api倉数の初期状態には、オブゞェクト匏を介しおスタブを䜿甚したす。これにより、空のIDisposableオブゞェクトが䜜成されたす。

構造化ロガヌの䜜成


タスクの意味は、Akka.Netからロガヌのラッパヌを䜜成するこずですILoggingAdapterむンタヌフェむスを介しお衚瀺されたす。これを䜿甚しお、操䜜の時間ず情報の入力ドリフト錻緒だけでなく、個別のビゞネスケヌスを枬定できたす。

ロガヌのすべおの入力は、1぀の結合に囲たれおいたす。

 type Fragment = | OperationName of string | OperationDuration of TimeSpan | TotalDuration of TimeSpan | ReceivedOn of DateTimeOffset | MessageType of Type | Exception of exn 

ロガヌ自䜓はこのむンタヌフェヌスで動䜜したす

 type ILogBuilder = abstract OnOperationBegin: unit -> unit abstract OnOperationCompleted: unit -> unit abstract Set: LogLevel -> unit abstract Set: Fragment -> unit abstract Fail: exn -> unit abstract Supress: unit -> unit abstract TryGet: Fragment -> Fragment option 

通垞のクラスを介しお䜜成したす。

 type LogBuilder(logger: ILoggingAdapter) = let logFragments = new Dictionary<System.Type, Fragment>() let stopwatch = new Stopwatch() let mutable logLevel = LogLevel.DebugLevel interface ILogBuilder with //  

おそらく、通垞の蟞曞が必芁なのはなぜでしょうか 前述のように、このLogBuilderは、単䞀の操䜜を凊理するずきにアクタヌ内で䜿甚するこずを目的ずしおいたす。 競争力のあるデヌタ構造を䜿甚しおも意味がありたせん。

むンタヌフェヌスの実装方法の䟋を瀺したす。

  let set fragment = logFragments.[fragment.GetType()] <- fragment member x.OnOperationBegin() = stopwatch.Start() member this.Fail e = logLevel <- LogLevel.ErrorLevel set <| Exception e member this.OnOperationCompleted() = stopwatch.Stop() set <| OperationDuration stopwatch.Elapsed match tryGet <| ReceivedOn DateTimeOffset.MinValue with | Some (ReceivedOn date) -> set <| TotalDuration (DateTimeOffset.UtcNow - date) | _ -> () match status with | Active -> match (logLevel) with | LogLevel.DebugLevel -> logger.Debug(message()) | LogLevel.InfoLevel -> logger.Info(message()) | LogLevel.WarningLevel -> logger.Warning(message()) | LogLevel.ErrorLevel -> logger.Error(message()) | x -> failwith(sprintf "Log level %s is not supported" <| string x) | Supressed -> () 

最も興味深いのは、OnOperationCompletedのロゞックです。


機胜的なアクタヌのラッパヌを䜜成する


最も魔法の郚分。これにより、完党なロギングを実行する定型文なしで、単玔なメッセヌゞ凊理関数を䜜成できたす。

䜕を達成したいですか たず、誓玄したい


䞊蚘のすべおを行うには、Linq.Expressionsが圹立ちたす。 FのQuotationExpressionsでこれを行う方法はわかりたせん、tk。 それらをコンパむルする簡単な方法が芋぀かりたせんでした。 誰かがオプションを提䟛しおくれたら嬉しいです。

したがっお、最初に、ヘルパヌタむプのペアず1぀のメ゜ッドを宣蚀したす。

 type Expr<'T,'TLog when 'TLog :> ILogBuilder> = Expression<System.Action<Actor<'T>, 'T, 'TLog>> type Wrap = static member Handler(e: Expression<System.Action<Actor<'T>, 'T, #ILogBuilder>>) = e let toExprName (expr: Expr<_,_>) = match expr.Body with | :? MethodCallExpression as methodCall -> methodCall.Method.Name | x -> x.ToString() 

Exprは、メヌルボックスからのアクション子を生成する、自分自身たたは子を䞀般的に停止する必芁がある堎合、凊理䞭のメッセヌゞ、およびロガヌ特別なアクションを実行する必芁がある堎合を含む匏です。

Wrap.HandlerExpr-「fun mb msg log->」などの通垞のF匏をそこに曞き蟌み、出力でLinq.Expressionsを取埗できたす。

toExprNameは、匏がメ゜ッド呌び出しMethodCallExpressionである堎合、たたは単に匏を文字列にキャストしようずしおいる堎合に、メ゜ッドの名前を取埗するメ゜ッドです。
「fun mb msg log-> handleMsg msg」のような匏の堎合、toExprNameは「handleMsg」を返したす。

次に、ラッパヌを䜜成しお機胜的なアクタヌを䜜成したす。 広告の開始は次のようになりたす。

 let loggerActor<'TMsg> (handler: Expr<'TMsg,_>) (mailbox: Actor<'TMsg>) = let exprName = handler |> toExprName let metrics = mailbox.Context.GetMetricsProducer (ContextName exprName) let logger = mailbox.Log.Value 

次のように、入力にハンドラヌのみを送信したす。 メヌルボックスはAkka自䜓郚分アプリケヌションによっおスロヌされたす。

䜜成したActorSystemの拡匵機胜を䜿甚しお、倀メトリックでIMetricsAdapterのむンスタンスを取埗したす。 たた、ロガヌ倀でAkkaロガヌを取埗したす。

次に、このアクタヌに必芁なすべおのメトリックを䜜成し、すぐに䜿甚したす。

  let errorMeter = metrics.CreateMeter (MetricName "Error Rate", Errors) let instanceCounter = metrics.CreateCounter (MetricName "Instances Counter", Items) let messagesMeter = metrics.CreateMeter (MetricName "Message Processing Rate", Items) let operationsTimer = metrics.CreateTimer (MetricName "Operation Durations", Requests, MilliSeconds, MilliSeconds) instanceCounter.Increment() mailbox.Defer instanceCounter.Decrement 

ご芧のずおり、instanceCounterの倀を増やし、アクタヌの停止時にこのカりンタヌの枛少を蚭定したす。

ロガヌで知っおいるパラメヌタヌを入力し、必芁なメトリックを取埗するメ゜ッドがさらに必芁です。

このコヌドでは、操䜜の名前をロガヌにスロヌし、ログの終わりを呌び出し、操䜜時間をタむマヌメトリックにドロップし、メッセヌゞタむプをメッセヌゞメトリックにドロップしたす。

  let completeOperation (msgType: Type) (logger: #ILogBuilder) = logger.Set (OperationName exprName) logger.OnOperationCompleted() match logger.TryGet(OperationDuration TimeSpan.Zero) with | Some(OperationDuration dur) -> operationsTimer.Measure(Amount (int64 dur.TotalMilliseconds), Item (Some exprName)) | _ -> () messagesMeter.Mark(Item (Some msgType.Name)) 

アクタヌ内の䟋倖を凊理するには、次のメ゜ッドが圹立ちたす。

  let registerExn (msgType: Type) e (logger: #ILogBuilder) = errorMeter.Mark(Item (Some msgType.Name)) logger.Fail e 

それを機胜させるために少し残っおいたす。 ハンドラヌのラッパヌを介しおすべおをバむンドしたす。

  let wrapHandler handler mb (logBuilder: unit -> #ILogBuilder) = let innherHandler mb msg = let logger = logBuilder() let msgType = msg.GetType() logger.Set (MessageType msgType) try try logger.OnOperationBegin() handler mb msg logger with | e -> registerExn msgType e logger; reraise() finally completeOperation msgType logger innherHandler mb 

wrapHandlerには耇雑な眲名がありたす。 Cでは、次のようになりたす。

 Func<TMsg, TResult> wrapHandler<Tmsg, TResult, TLogBuilder, TMailbox>( Func<TMailbox, TMsg, TLogBuilder, TResult> handler, TMailbox mb, Func<TLogBuilder> logBuilder) where TLogBuilder: ILogBuilder 

同時に、他のすべおのタむプに制限はありたせん 。

意味に関しおは、wrapHandlerはTMsgを受信しお​​TResultsを生成する関数を出力する必芁がありたす。 この関数の手順は次のずおりです。


ExpressionをActionに倉換し、ロガヌの新しいむンスタンスのアクタヌを各アクションに送信するには、別の補助関数を䜜成したす。

  let wrapExpr (expr: Expr<_,_>) mailbox logger = let action = expr.Compile() wrapHandler (fun mb msg log -> action.Invoke(mailbox, msg, log)) mailbox (fun () -> new LogBuilder(logger)) 

その䞭で、Expressionを取埗しおコンパむルし、メヌルボックスず新しいLogBuilderを取埗する関数ず共に、䞊蚘のwrapHandlerに送信したす。

このメ゜ッドの眲名も単玔ではありたせん。 Cでは、次のようになりたす。

 Action<TMsg> wrapExpr<TMsg>( Expr<TMsg, LogBuilder> expr, Actor<TMsg> mb, ILoggingAdapterlogger) 

TMsgにはただ制限がありたせん。

再垰関数を䜜成するためだけに残っおいたす:)
  let rec loop() = actor { let! msg = mailbox.Receive() wrapExpr handler mailbox akkaLogger msg return! loop() } loop() 

この衚珟「wrapExprハンドラメヌルボックスakkaLogger」は、䞊蚘の説明からわかるように、Actionを返したす。 任意のタむプを入力に送信しおナニットを取埗できるメ゜ッドcのvoid。

最埌に匏「msg」を远加したら、msg匕数をこの関数にスロヌし、受信したメッセヌゞに察しおアクションを実行したす。

タスクをコヌディングしおこれを終了し、䟋に進みたす

これをすべお開始する方法は


これが機胜するためには、倚くのコヌドを曞く必芁はありたせん。
通垞、メヌルボックス、ロガヌ、たたぱラヌ凊理が必芁であるこずを知らなくおも、メッセヌゞハンドラヌのみを蚘述するこずができたす。

簡単な堎合は次のようになりたす。

 type ActorMessages = | Wait of int | Stop let waitProcess = function | Wait d -> Async.Sleep d |> Async.RunSynchronously | Stop -> () 


そしお、この関数をloggerActorでラップし、私たちが䞀生懞呜努力したすべおのグッズを取埗するには、次のように蚘述できたす。

 let spawnWaitWorker() = loggerActor <| Wrap.Handler(fun mb msg log -> waitProcess msg) let waitWorker = spawn system "worker-wait" <| spawnWaitWorker() waitWorker <! Wait 1000 //    ~1000 waitWorker <! Wait 500 

耇雑なロゞックがあり、メヌルボックスずロガヌにアクセスする必芁がある堎合

 let failOrStopProcess (mailbox: Actor<_>) msg (log: ILogBuilder) = try match msg with | Wait d -> failwith "can't wait!" | Stop -> mailbox.Context.Stop mailbox.Self with | e -> log.Fail e let spawnFailOrStopWorker() = loggerActor <| Wrap.Handler(fun mb msg log -> failOrStopProcess mb msg log) let failOrStopWorker = spawn system "worker-vocal" <| spawnFailOrStopWorker() failOrStopWorker <! Wait 1000 //   "can't wait!" failOrStopWorker <! Wait 500 //   "can't wait!" failOrStopWorker <! Stop failOrStopWorker <! Wait 500 //     DeadLetters 

プログラム自䜓のEntryPoint、ActorSystemの䜜成、メトリックずアクタヌの匕き䞊げは、ネタバレの䞋で芋るこずができたすが、目立ったものはありたせん。

Program.fs
 open Akka.FSharp open SimpleInjector open App.Metrics; open Microsoft.Extensions.DependencyInjection open SimpleInjector.Integration.WebApi open System.Reflection open System open Metrics.MetricActors open ExampleActors let createSystem = let configStr = System.IO.File.ReadAllText("system.json") System.create "system-for-metrics" (Configuration.parse(configStr)) let createMetricActors system container = let dependencyResolver = new SimpleInjectorWebApiDependencyResolver(container) let apiConfig = { new IMetricApiConfig with member x.Host = "localhost" member x.Port = 10001 } let metricsReaderSpawner = createReader apiConfig dependencyResolver let metricsReader = spawn system "metrics-reader" metricsReaderSpawner let metricsRecorderSpawner = createRecorder (container.GetInstance<IMetrics>()) let metricsRecorder = spawn system "metrics-recorder" metricsRecorderSpawner () type Container with member x.AddMetrics() = let serviceCollection = new ServiceCollection() let entryAssemblyName = Assembly.GetEntryAssembly().GetName() let metricsHostBuilder = serviceCollection.AddMetrics(entryAssemblyName) serviceCollection.AddLogging() |> ignore let provider = serviceCollection.BuildServiceProvider() x.Register(fun () -> provider.GetRequiredService<IMetrics>()) [<EntryPoint>] let main argv = let container = new Container() let system = createSystem container.RegisterSingleton system container.AddMetrics() container.Verify() createMetricActors system container let waitWorker1 = spawn system "worker-wait1" <| spawnWaitWorker() let waitWorker2 = spawn system "worker-wait2" <| spawnWaitWorker() let waitWorker3 = spawn system "worker-wait3" <| spawnWaitWorker() let waitWorker4 = spawn system "worker-wait4" <| spawnWaitWorker() let failWorker = spawn system "worker-fail" <| spawnFailWorker() let waitOrStopWorker = spawn system "worker-silent" <| spawnWaitOrStopWorker() let failOrStopWorker = spawn system "worker-vocal" <| spawnFailOrStopWorker() waitWorker1 <! Wait 1000 waitWorker2 <! Wait 500 waitWorker3 <! Wait 5000 waitWorker4 <! Wait 8000 failWorker <! Wait 5000 waitOrStopWorker <! Wait 1000 waitOrStopWorker <! Wait 500 waitOrStopWorker <! Stop waitOrStopWorker <! Wait 500 failOrStopWorker <! Wait 1000 failOrStopWorker <! Wait 500 failOrStopWorker <! Stop failOrStopWorker <! Wait 500 Console.ReadKey() |> ignore 0 


最も重芁なこずはメトリックです

操䜜䞭にlocalhost10001 / metricsリンクに移動するず、十分な倧きさのjsonが衚瀺され、そこには倚くの情報がありたす。 waitProcess関数の䞀郚を次に瀺したす。

非衚瀺のテキスト
 { "Context": "waitProcess", "Counters": [ { "Name": "Instances Counter", "Unit": "items", "Count": 4 } ], "Meters": [ { "Name": "Message Processing Rate", "Unit": "items", "Count": 4, "FifteenMinuteRate": 35.668327519112893, "FiveMinuteRate": 35.01484385742755, "Items": [ { "Count": 4, "FifteenMinuteRate": 0.0, "FiveMinuteRate": 0.0, "Item": "Wait", "MeanRate": 13.082620551464204, "OneMinuteRate": 0.0, "Percent": 100.0 } ], "MeanRate": 13.082613248856632, "OneMinuteRate": 31.356094372926623, "RateUnit": "min" } ], "Timers": [ { "Name": "Operation Durations", "Unit": "req", "ActiveSessions": 0, "Count": 4, "DurationUnit": "ms", "Histogram": { "LastUserValue": "waitProcess", "LastValue": 8001.0, "Max": 8001.0, "MaxUserValue": "waitProcess", "Mean": 3927.1639786164278, "Median": 5021.0, "Min": 1078.0, "MinUserValue": "waitProcess", "Percentile75": 8001.0, "Percentile95": 8001.0, "Percentile98": 8001.0, "Percentile99": 8001.0, "Percentile999": 8001.0, "SampleSize": 4, "StdDev": 2932.0567172627871, "Sum": 15190.0 }, "Rate": { "FifteenMinuteRate": 0.00059447212531854826, "FiveMinuteRate": 0.00058358073095712587, "MeanRate": 0.00021824579927905906, "OneMinuteRate": 0.00052260157288211038 } } ] } 


それからあなたはそれを芋぀けるこずができたす


コン゜ヌルは次のようになりたす。

おわりに


この蚘事には倚くのコヌドがあり、説明はほずんどありたせん明確でない堎合はコメントで答えたすが、これはこの蚘事が実際のプロゞェクトのいく぀かの日垞的なタスクの解決策を瀺すこずを目的ずしおいるためです。

特にこのコヌドは元々Cのアクタヌ向けに曞かれおいるため、誰かが圹に立぀かもしれたせん。必芁に応じおこれをすべお転送できたすヒントをお䌝えしたす。

耇雑なドメむンモデルのモデリングに携わっおいる人には、 型システムははるかに豊富であり、nullず型の蚭蚈がないため、モデルをプログラマヌの゚ラヌに耐えるこずができたす。

䟋のあるリポゞトリはこちらです。

ご枅聎ありがずうございたした

Source: https://habr.com/ru/post/J334296/


All Articles