2017-03-02 46 views
1

我有一个POST控制器方法使用异步等待在很多服务和控制器级别我需要发送一些新的遗物参数。 New Relic在参数从非请求启动的线程发送时发出警告日志。如何在同一个线程上调用请求开始

NewRelic WARN: Agent API Error: An error occurred invoking API method "AddCustomParameter" - "System.InvalidOperationException: The API method called is only valid from within a transaction. This error can occur if you call the API method from a thread other than the one the transaction started on. at NewRelic.Agent.Core.Api.AsyncAgentApi.GetCurrentTransactionBuilder()
at NewRelic.Agent.Core.Api.AsyncAgentApi.AddCustomParameter(String key, String value)"

如何在我的控制器方法中调用将参数值发送到New Relic的代码?

例如,下面的代码在控制器中。

var threadid = Thread.CurrentThread.ManagedThreadId; 
Log.Debug($"Before async method : {ThreadIdMessage(threadid)}"); 
var reportObject = await ReportService.GetReportAsync(requestModel).ConfigureAwait(true); 
if (reportObject.PolicyModels != null) 
{ 
    threadid = Thread.CurrentThread.ManagedThreadId; 
    Log.Debug($"Before sending New Relic values: {ThreadIdMessage(threadid)}"); 
    AddPoliciesCountInNewRelic(reportObject.PolicyModels.Count); 
    AddTotalTransactionsCountInNewRelic(
          reportObject.PolicyModels.SelectMany(p => p.PolicyTransactionModels).Count()); 
    threadid = Thread.CurrentThread.ManagedThreadId; 
    Log.Debug($"After sending New Relic values: {ThreadIdMessage(threadid)}"); 
} 

将打印

DEBUG - Before async method : Current Thread Id: 5 
DEBUG - Before sending New Relic values: Current Thread Id: 9 
NewRelic.AddCustomParameter(CVPoliciesCount,2) 
NewRelic.AddCustomParameter(CVTotalTransactionsCount,8) 
DEBUG - After sending New Relic values: Current Thread Id: 9 

每New Relic的警告日志,我应该在调用线程ID的AddCustomParameter方法5.

AddPoliciesCountInNewRelicAddTotalTransactionsCountInNewRelic调用ApiControllerBase.AddNewRelicParameter(string, string)基类的保护方法。

private void AddPoliciesCountInNewRelic(int policiesCount) 
{ 
    AddNewRelicParameter("CVPoliciesCount", policiesCount.ToString()); 
} 

private void AddTotalTransactionsCountInNewRelic(int transactionsCount) 
{ 
    AddNewRelicParameter("CVTotalTransactionsCount", transactionsCount.ToString()); 
} 

protected void AddNewRelicParameter(string key, string value) 
{ 
    if (!string.IsNullOrWhiteSpace(key) && 
     !string.IsNullOrWhiteSpace(value)) 
    { 
     try 
     { 
      NewRelic.Api.Agent.NewRelic.AddCustomParameter(key, value); 
     } 
     catch (Exception ex) 
     { 
      Log.Error($"ERROR! : New Relic Parameter Exception {ex}"); 
     } 
    } 
} 
+1

https://discuss.newrelic.com/t/are-custom-params-supported-in-async-mode/376​​01 <他们声称现在支持异步控制器。 – Evk

+2

asp.net中的'ConfigureAwait(true)'并不能保证你回到开始调用的那个线程,就像你在Winforms或WPF中使用UI线程时一样。 –

+0

实际上'ConfigureAwait(true)'是默认行为。它和'等待ReportService.GetReportAsync(requestModel);'完全一样。 –

回答

1

我能够解决使用continuation的问题,但像@evk在@ Tim回答说,New Relic应该考虑解决这个问题,以便我们不必解决适当的代码以满足此类要求。

var context = TaskScheduler.FromCurrentSynchronizationContext();  
    var threadid = Thread.CurrentThread.ManagedThreadId; 
    Log.Debug($"Entry ThreadID: {threadid}"); 
    var getReportTask = ReportService.GetReportAsync(requestModel); 
    getReportTask.ContinueWith(antecedent => 
    { 
     var continuationThreadid = Thread.CurrentThread.ManagedThreadId; 
     Log.Debug($"continuationThreadid: {continuationThreadid}"); 
     var result = antecedent.Result; 
     if (result.PolicyModels != null) 
     { 
      AddPoliciesCountInNewRelic(result.PolicyModels.Count); 
      AddTotalTransactionsCountInNewRelic(
       result.PolicyModels.SelectMany(p => p.PolicyTransactionModels).Count()); 
     } 
    }, context); 

    var reportObject = await getReportTask.ConfigureAwait(false); 

它将按预期打印相同的线程ID。

DEBUG - Entry ThreadID: 5 
DEBUG - continuationThreadid: 5 
+1

在这种情况下,为什么不使用API​​的* synchronous *版本?这样你就不会冒着另一个请求冒你的线程并迫使你等待它的风险。 PS - 在ASP.NET中,同步上下文是* not *线程。你可能*仍然*得到一个不同的线程。 –

+0

@PanagiotisKanavos和你做的。我自己尝试过,延续并不在最初的线程中。 SynchronizationContext是AspNetSynchronizationContext,如果您查找post方法的源代码,它将导致使用TaskScheduler.Default [source](https://referencesource.microsoft.com/#System.Web/AspNetSynchronizationContext.cs,f0184c54fac66559) – Blim

0

如果你一定要得到相同的线程,那么你真的不能说thread--没有保证您的线程将回来给你退出。 Async ... await将正确地恢复您的操作请求的上下文,但不一定是同一个线程。

换句话说,要么不要让异步...等待调用并保持线程,要么在控制器方法中启动异步代码,而是阻止并等待它完成(并因此而失败async..await代码,因为当你等待你的IO完成时,你将不会产生另一个操作方法的线程)

+0

或向NewRelic提出问题并要求他们修正其代码。 – Evk

+0

@evk正是......由于他们的要求,无法击败我的节目。 –

相关问题