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

SSIS Script Task中client.GetAsync未等待API响应问题排查

SSIS Script Task 中异步API调用直接返回不等待响应的问题

问题描述

在SSIS script task中使用client.GetAsync(url)发起API调用时,程序不会等待API返回响应,而是直接跳转回脚本任务入口方法public void Main()。经排查疑似异步死锁,但公开可查的常规修复方案均未生效。相同代码在网页端运行正常,可正常等待响应后执行后续逻辑。

调试观测现象

  • 脚本入口处获取的payload变量状态为:ID =5, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
  • 调试模式下重新执行流程时可观测到2个线程:一个为当前执行线程,另一个是首次调用生成的旧线程,旧线程中已包含首次调用期望获取的响应数据,该现象成因不明。

相关代码

Script Task 入口代码

public void Main() {
  // TODO: Add your code here
  try {
    PackageDetails packageInfo = new PackageDetails {
      PackageNumber = 1234567891, Env = "Development", UserName = "USER"
    };
    var payload = API.ListHeadByPackAsync(packageInfo);
    //var test = GetResponse();
    Dts.TaskResult = (int) ScriptResults.Success;
  } catch (Exception ex) {
    System.Console.Write(ex.Message);
    Dts.TaskResult = (int) ScriptResults.Failure;
  }
}

API调用层代码

public static class API {
  public static async Task<PackageDetails> ListHeadByPackAsync(PackageDetails package) {
    PackageDetails packageInfo = new PackageDetails();
    try {
      using(var client = new ApiClient(requestUrl, authToken)) {
        var response = await client.GetAsync(); //-> 此处不会等待响应返回
      }
    } catch (Exception err) {
      switch (err.Message) {
        //TODO:
      }

    }
    return packageInfo;
  }
}

ApiClient实现代码

public class ApiClient: IDisposable {
  private readonly TimeSpan _timeout;
  private HttpClient _httpClient;
  private HttpClientHandler _httpClientHandler;
  private readonly string _baseUrl;
  private readonly string _credentials;
  //private const string MediaTypeXml = "application/csv";

  public ApiClient(string baseUrl, string authToken, TimeSpan? timeout = null) {
    _baseUrl = baseUrl;
    _credentials = Base64Encode(authToken);
    _timeout = timeout ?? TimeSpan.FromSeconds(90);
  }
  public async Task<string> GetAsync() {
    EnsureHttpClientCreated();
    using(var response = await _httpClient.GetAsync(_baseUrl).ConfigureAwait(continueOnCapturedContext: false))
    //-> 执行完上一行代码后直接跳回Main()方法,不等待响应
    {
      response.EnsureSuccessStatusCode();
      return await response.Content.ReadAsStringAsync();
    }
  }
  public void Dispose() {
    _httpClientHandler?.Dispose();
    _httpClient?.Dispose();
  }
  private void CreateHttpClient() {
    _httpClientHandler = new HttpClientHandler {
      AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
    };
    _httpClient = new HttpClient(_httpClientHandler, false) {
      Timeout = _timeout
    };
    if (!string.IsNullOrWhiteSpace(_baseUrl)) {
      _httpClient.BaseAddress = new Uri(_baseUrl);
    }
    _httpClient.DefaultRequestHeaders.Add("Authorization", "Basic" + " " + _credentials);
  }
  private void EnsureHttpClientCreated() {
    if (_httpClient == null) {
      //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
      CreateHttpClient();
    }
  }
  public static string Base64Encode(string token) {
    var tokenBytes = System.Text.Encoding.UTF8.GetBytes(token);
    return System.Convert.ToBase64String(tokenBytes);
  }
}

问题根因

该问题并非异步死锁,核心原因是代码未等待异步任务执行完成。SSIS Script Task的入口Main()是同步方法,在同步方法中调用返回Task类型的异步方法时,若未对返回的Task做等待操作,代码执行到var payload = API.ListHeadByPackAsync(packageInfo);时仅会触发异步方法启动,随后立刻向下执行,直接走到设置Dts.TaskResult = Success的逻辑,整个脚本任务随即执行结束,不会等待异步HTTP请求完成。
相同代码在网页端运行正常,是因为网页端异步代码通常运行在异步上下文(如async的Page生命周期、async的Controller方法)中,会天然等待await标记的任务执行完成;但SSIS的VSTA脚本环境是类控制台的同步执行上下文,不会自动等待未被显式等待的Task。
调试时观测到的旧线程残留,是因为前一次执行脚本任务时进程直接退出,异步请求仍在后台线程运行,下次调试附加进程时,上一次启动的后台线程刚好执行完成拿到响应数据,因此能在旧线程中看到预期的返回结果。

修复方案

由于SSIS脚本任务入口不支持async Main(低版本VSTA甚至不支持C# 7.1及以上的async入口写法),兼容性最高的方案是在调用异步方法时显式同步等待任务执行完成。
将Main方法中调用API的代码修改为:

var payload = API.ListHeadByPackAsync(packageInfo).GetAwaiter().GetResult();

注意:不要使用.Wait()或直接访问.Result属性,这两种写法会将原始异常包裹在AggregateException中,不利于后续错误排查,GetAwaiter().GetResult()会直接抛出原始异常。
另外代码中两处逻辑需要同步修正:

  • ListHeadByPackAsync方法中拿到response后未将内容赋值给packageInfo,即使等待完成也会返回空对象,需要将响应内容反序列化后赋值给返回值。
  • GetAsync方法中response.Content.ReadAsStringAsync()建议同样加上.ConfigureAwait(false),和前置逻辑保持一致,避免不必要的上下文捕获。

内容的提问来源于stack exchange,提问作者QuickSilver

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:48:16