Graph API认证令牌重叠疑问及TaskCanceledException排查
问题背景与疑问
我们在Azure中部署了拥有全部所需权限的应用“App1”,同时运行着7个C# Windows服务实例,这些实例均使用该Azure应用连接Outlook邮箱,仅配置存在差异——每个实例对应连接一个唯一的邮箱。
疑问1:令牌是否会重叠?
各服务实例运行在独立的Windows服务器上,当所有服务执行如下认证代码时,是否会因共用Azure应用“App1”导致令牌重叠?还是因每个实例连接唯一邮箱,会获取到各自独立的访问令牌?
认证代码:
AuthenticationResult authResult = null; GraphServiceClient graphClient = null; IMailFolderMessagesCollectionPage inboxMessage = null; try { string tenantId = "TenantId"; string clientId = "ClientId"; string clientSecret = "ClientSecret"; var authority = $"https://login.microsoftonline.com/{tenantId}"; var app = ConfidentialClientApplicationBuilder .Create(clientId) .WithClientSecret(clientSecret) .WithAuthority(new Uri(authority)) .Build(); var scopes = new[] { "https://graph.microsoft.com/.default" }; authResult = await app.AcquireTokenForClient(scopes).ExecuteAsync(); } catch (Exception ex) { //return; } if (authResult != null) { graphClient = new GraphServiceClient( new DelegateAuthenticationProvider(requestMessage => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken); return Task.FromResult(0); }));
疑问2:TaskCanceledException的原因?
此外,我们频繁遇到如下错误,想了解当前架构是否会影响应用,若不是,该错误的可能原因是什么?
错误日志:
Message: System.Threading.Tasks.TaskCanceledException: A task was canceled. at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Graph.HttpProvider.<SendRequestAsync>d__19.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Graph.HttpProvider.<SendAsync>d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Graph.BaseRequest.<SendRequestAsync>d__40.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Graph.BaseRequest.<SendAsync>d__34`1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Graph.MessageAttachmentsCollectionRequest.<GetAsync>d__3.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at GraphPOC.EmailMain.<GetDataAsync>d__63.MoveNext() Category: Error Priority: -1 EventId: 0 Severity: Error Title: App Domain: GraphPOC.exe ProcessId: 988 Process Name: D:\Program Files\GraphPOC.exe Thread Name: Win32 ThreadId:4884 Extended Properties: Callstack= at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) at System.Environment.get_StackTrace() at System.Diagnostics.TraceEventCache.get_Callstack() at System.Diagnostics.TraceListener.WriteFooter(TraceEventCache eventCache) at Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedTextWriterTraceListener.TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, Object data) at Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener.TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, Object data) at Microsoft.Practices.EnterpriseLibrary.Logging.LogSource.TraceData(TraceEventType eventType, Int32 id, LogEntry logEntry, TraceListenerFilter traceListenerFilter) at Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter.ProcessLog(LogEntry log) at Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter.Write(LogEntry log) at GraphPOC.EmailMain.<GetDataAsync>d__63.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord, Object cancellationException) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception) at Microsoft.Graph.MessageAttachmentsCollectionRequest.<GetAsync>d__3.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord, Object cancellationException) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception) at Microsoft.Graph.BaseRequest.<SendAsync>d__34`1.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord, Object cancellationException) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception) at Microsoft.Graph.BaseRequest.<SendRequestAsync>d__40.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord, Object cancellationException) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception) at Microsoft.Graph.HttpProvider.<SendAsync>d__18.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord, Object cancellationException) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception) at Microsoft.Graph.HttpProvider.<SendRequestAsync>d__19.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord, Object cancellationException) at System.Threading.Tasks.Task`1.TrySetCanceled(CancellationToken tokenToRecord) at System.Threading.Tasks.TaskCompletionSource`1.TrySetCanceled(CancellationToken cancellationToken) at System.Net.Http.HttpClient.SetTaskCanceled(HttpRequestMessage request, CancellationTokenSource cancellationTokenSource, TaskCompletionSource`1 tcs) at System.Net.Http.HttpClient.<>c__DisplayClass59_0.<StartContentBuffering>b__0(Task contentTask) at System.Threading.Tasks.Task.Execute() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot) at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution) at System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued) at System.Threading.Tasks.TaskScheduler.TryRunInline(Task task, Boolean taskWasPreviouslyQueued) at System.Threading.Tasks.TaskContinuation.InlineIfPossibleOrElseQueue(Task task, Boolean needsProtection) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task.Finish(Boolean bUserDelegateExecuted) at System.Threading.Tasks.Task`1.TrySetException(Object exceptionObject) at System.Threading.Tasks.TaskCompletionSource`1.TrySetException(Exception exception) at System.Net.Http.HttpContent.<>c__DisplayClass21_0.<LoadIntoBufferAsync>b__0(Task copyTask) at System.Threading.Tasks.Task.Execute() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot) at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution) at System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued) at System.Threading.Tasks.TaskScheduler.TryRunInline(Task task, Boolean taskWasPreviouslyQueued) at System.Threading.Tasks.TaskContinuation.InlineIfPossibleOrElseQueue(Task task, Boolean needsProtection) at System.Threading.Tasks.Task.FinishContinuations() at System.Threading.Tasks.Task.Finish(Boolean bUserDelegateExecuted) at System.Threading.Tasks.Task`1.TrySetException(Object exceptionObject) at System.Threading.Tasks.TaskCompletionSource`1.TrySetException(Exception exception) at System.Net.Http.StreamToStreamCopy.BufferReadCallback(IAsyncResult ar) at System.IO.Compression.DeflateStreamAsyncResult.Complete(Object result) at System.IO.Compression.DeflateStream.ReadCallback(IAsyncResult baseStreamResult) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.ChunkParser.ReadCallback(IAsyncResult ar) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.FixedSizeReader.ReadCallback(IAsyncResult transportResult) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Net.ContextAwareResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
解答
关于令牌重叠问题
不会出现令牌重叠的情况,每个服务实例都会获取到独立的访问令牌,原因如下:
- 你使用的是客户端凭据流(Client Credentials Flow),调用
AcquireTokenForClient获取的令牌基于应用身份(App1)颁发,而非用户身份。但每个服务实例是独立运行的进程,各自维护自己的令牌获取和缓存逻辑,实例之间不会共享令牌。 - 即使所有实例共用同一个App1,每次
AcquireTokenForClient调用都是独立向Azure AD发起的请求,返回的令牌是独立的(内容可能相似,但实例间无干扰)。 - 每个实例连接不同邮箱,是在后续调用Graph API时指定目标邮箱(比如
/users/{邮箱地址}/messages),令牌本身不绑定特定邮箱,只要App1拥有访问该邮箱的权限即可。
关于TaskCanceledException的原因
当前架构不会直接导致这个错误,该错误的常见原因包括:
- 请求超时:Graph API请求超过了默认超时时间(HttpClient或GraphServiceClient默认超时通常为100秒,若网络慢或请求数据量大,易触发超时)。
- 网络不稳定:服务器与Microsoft Graph服务之间的连接中断、延迟过高,导致请求被取消。
- 并发请求限制:短时间内发起大量请求可能触发Microsoft Graph的限流机制,部分情况下会表现为任务取消(通常限流返回429错误,但也有例外)。
- 代码问题:代码中手动调用了
CancellationToken的取消方法,或异步操作的上下文管理不当。
排查建议
- 调整
GraphServiceClient的超时时间,示例:
var httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromMinutes(5); // 根据实际需求调整 var graphClient = new GraphServiceClient(httpClient, new DelegateAuthenticationProvider(...));
- 添加重试机制,针对网络超时或临时错误进行重试,可使用Polly等工具实现。
- 监控服务器网络状态,检查是否存在丢包、延迟过高的情况。
- 查看Microsoft Graph调用日志,确认是否有429限流错误,若有则调整请求频率。
相关产品推荐
相关产品推荐

