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

Docker容器中Discord.NET调用DeferAsync后斜杠命令全部超时无响应

问题现象

基于discord.net开发的Discord机器人托管在Docker容器中运行,初始版本/getmoney斜杠命令可正常响应,对应实现代码如下:

[SlashCommand("getmoney", "get current total of currency for given user")]
[DefaultPermission(true)]
public async Task GetMoney(Discord.IUser user) 
{
    var userHelper = new UserHelper(_dbContext);
    var userRecord = userHelper.FindOrAddUser(user.Id.ToString());

    await ReplyAsync($"{user.Username} has {userRecord.Funds} Dollars!");
}

引入DeferAsync()与FollowupAsync()改造命令逻辑后,本地调试环境运行完全正常,对应改造后代码如下:

[SlashCommand("getmoney", "get current total of currency for given user")]
[DefaultPermission(true)]
public async Task GetMoney(Discord.IUser user) 
{
    await DeferAsync();

    var userHelper = new UserHelper(_dbContext);
    var userRecord = userHelper.FindOrAddUser(user.Id.ToString());

    await FollowupAsync($"{user.Username} has {userRecord.Funds} Dollars!");
}

但将改造后的代码构建镜像部署到容器后,所有斜杠命令均出现超时问题,包括其他独立模块的命令也无法正常响应;触发命令时日志无任何错误输出。回退到改造前的代码重新构建部署后服务恢复正常,可确认代码修改是问题诱因,但无对应错误日志可追溯。

根因

该问题是典型的Discord.NET网关线程死锁问题:

  • Discord.NET默认斜杠命令执行模式为RunMode.Sync,即命令处理逻辑直接在网关WebSocket事件循环线程上同步执行
  • 调用DeferAsync()时,方法会向Discord API发送交互延迟确认请求,同时需要等待网关线程接收API返回的ACK响应才能继续往下执行
  • 此时网关线程正被阻塞在等待DeferAsync()完成的逻辑上,无法从WebSocket连接读取返回的ACK消息,直接造成整个事件循环卡死
  • 本地调试环境运行时,Debug编译配置会附带额外的线程调度兜底逻辑,加上桌面系统的线程池调度策略和Linux容器环境存在差异,因此不会触发死锁;Release编译后在Linux容器中运行时无兜底逻辑,死锁稳定复现,且因为线程直接卡死,不会抛出任何异常,因此没有错误日志。
修复方案

按优先级选择以下配置即可解决问题:

  1. 全局配置交互服务默认异步执行命令,从根源避免网关线程阻塞。在初始化InteractionService时传入配置:
    var interactionService = new InteractionService(client, new InteractionServiceConfig
    {
        DefaultRunMode = RunMode.Async
    });
    
  2. 若不想全局修改默认执行模式,可单独给存在长耗时操作、需要调用DeferAsync的命令添加特性,标记为异步执行:
    [SlashCommand("getmoney", "get current total of currency for given user")]
    [DefaultPermission(true)]
    [RunMode(RunMode.Async)]
    public async Task GetMoney(Discord.IUser user) 
    {
        await DeferAsync();
        var userHelper = new UserHelper(_dbContext);
        var userRecord = userHelper.FindOrAddUser(user.Id.ToString());
        await FollowupAsync($"{user.Username} has {userRecord.Funds} Dollars!");
    }
    
  3. Docker部署额外优化:
    • 发布dotnet项目时不要使用过度激进的程序集裁剪配置,避免异步调度相关依赖被误裁剪
    • 启动容器时可添加环境变量DOTNET_ThreadPoolMinThreads=200,调高Linux容器下dotnet线程池最小线程数,避免线程池饥饿加剧阻塞问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 11:36:28