Azure EventHub发送消息遇SocketException及未观察任务异常,如何处理?
我有一段向Azure EventHub发送消息的代码,起初运行正常:
public async Task SendMessage(string message) { var producer = new EventHubProducerClient(this.connectionString, this.eventHubName); using (EventDataBatch eventBatch = await producer.CreateBatchAsync()) { if (!eventBatch.TryAdd(new EventData(message))) { throw new Exception($"Event is too large for the batch and cannot be sent."); } try { await producer.SendAsync(eventBatch); } catch(Exception ex) { saveLog(message, ex); } finally { await producer.DisposeAsync(); } } }
但在Application Insights中出现以下异常信息:
A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
异常类型:
System.Net.Sockets.SocketException
我尝试添加ContinueWith(指定OnlyOnFaulted或OnlyOnCanceled)来记录日志,修改后的代码片段如下:
try { await producer.SendAsync(eventBatch) .ContinueWith(t => ExceptionHandle(t, message), TaskContinuationOptions.OnlyOnFaulted); }
但现在在catch块中捕获到“任务已取消”的异常(而非进入ExceptionHandle方法)。请问该如何处理这些在Application Insights中显示的异常?
1. 问题根源
- 最初的未观察任务异常,是因为**
producer.DisposeAsync()在SendAsync异步操作未完成时就被调用**:finally块里的释放操作提前终止了连接,导致SendAsync抛出的异常没有被当前上下文捕获,最终被终结器线程重新抛出。 - 添加
ContinueWith后出现“任务已取消”,是因为OnlyOnFaulted指定仅在原任务失败时执行延续任务,若原任务正常完成,延续任务会被标记为取消,await这个取消的任务就会触发TaskCanceledException。
2. 代码修复方案
方案一:修正资源释放顺序
将EventHubProducerClient放入using块,确保它在所有异步操作完成后才自动释放,避免提前断开连接:
public async Task SendMessage(string message) { // 用using包裹Producer,自动处理DisposeAsync,无需手动在finally中调用 using var producer = new EventHubProducerClient(this.connectionString, this.eventHubName); using (EventDataBatch eventBatch = await producer.CreateBatchAsync()) { if (!eventBatch.TryAdd(new EventData(message))) { throw new Exception($"Event is too large for the batch and cannot be sent."); } try { await producer.SendAsync(eventBatch); } catch(Exception ex) { saveLog(message, ex); // 若需要上层处理异常,可保留throw;否则无需抛出 // throw; } } }
方案二:简化异常处理,移除ContinueWith
async/await本身已提供清晰的异常处理流程,无需使用ContinueWith。可以针对不同异常类型做针对性处理:
public async Task SendMessage(string message) { using var producer = new EventHubProducerClient(this.connectionString, this.eventHubName); using (EventDataBatch eventBatch = await producer.CreateBatchAsync()) { if (!eventBatch.TryAdd(new EventData(message))) { throw new Exception($"Event is too large for the batch and cannot be sent."); } try { await producer.SendAsync(eventBatch); } catch(SocketException ex) { // 专门处理连接超时类的Socket异常 saveLog(message, ex); // 可选:添加重试逻辑,比如用Polly库实现指数退避重试 } catch(TaskCanceledException ex) { // 处理任务取消异常(比如超时导致的取消) saveLog(message, ex); } catch(Exception ex) { // 捕获其他所有异常 saveLog(message, ex); } } }
3. 额外优化建议
- 添加重试机制:针对Socket连接超时这类临时故障,使用Polly库实现重试策略,提升消息发送的可靠性。
- 配置操作超时:创建
EventHubProducerClient时,通过EventHubProducerClientOptions设置合理的操作超时时间,避免任务无限等待。 - 异步化日志操作:如果
saveLog是耗时操作,改为async Task并使用await调用,避免阻塞线程。
内容的提问来源于stack exchange,提问作者Programador Desenvolvedor

