You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 02:15:36