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

本地正常的C# .NET Core应用部署Azure WebApp后ServiceBus/SQL失效排查

解决Azure WebApp中ServiceBus读取和SQL写入失败的问题

看起来你遇到的问题核心是控制台应用的运行模式不匹配Azure WebApp的环境,再加上配置管理、资源生命周期的小问题。下面是具体的调整方案,一步步来解决:


1. 把控制台应用改成ASP.NET Core Worker Service

Azure WebApp默认会终止没有HTTP端点的后台进程,你的控制台应用靠Console.ReadKey()维持运行,但在WebApp环境中这个方法不会生效,进程会很快被系统回收。改成Worker Service(专门的后台服务模板)就能让应用在WebApp中长期稳定运行。

操作步骤:

  • 新建一个ASP.NET Core Worker Service项目(或者把现有控制台项目改造为Worker Service)
  • 把ServiceBus和SQL的业务逻辑移到Worker.cs的对应方法中

示例核心代码(Worker.cs):

using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace ServiceBusReaderWorker
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly string _serviceBusConnStr;
        private readonly string _queueName;
        private readonly string _sqlConnStr;
        private IQueueClient _queueClient;

        // 依赖注入获取配置和日志
        public Worker(ILogger<Worker> logger, IConfiguration configuration)
        {
            _logger = logger;
            _serviceBusConnStr = configuration["ServiceBusConnectionString"];
            _queueName = configuration["QueueName"];
            _sqlConnStr = configuration.GetConnectionString("SqlDatabase");
        }

        // 启动时初始化ServiceBus客户端
        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("ServiceBus reader starting...");
            _queueClient = new QueueClient(_serviceBusConnStr, _queueName);
            RegisterMessageHandler();
            await base.StartAsync(cancellationToken);
        }

        // 后台服务保持运行的核心逻辑
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            // 用无限延迟让服务持续运行,直到收到停止信号
            await Task.Delay(Timeout.Infinite, stoppingToken);
        }

        // 注册ServiceBus消息处理器
        private void RegisterMessageHandler()
        {
            var handlerOptions = new MessageHandlerOptions(ExceptionHandler)
            {
                MaxConcurrentCalls = 1,
                AutoComplete = false
            };
            _queueClient.RegisterMessageHandler(ProcessMessageAsync, handlerOptions);
        }

        // 处理ServiceBus消息并写入SQL
        private async Task ProcessMessageAsync(Message message, CancellationToken token)
        {
            var messageBody = Encoding.UTF8.GetString(message.Body);
            _logger.LogInformation("Received message: {seq} - {body}", message.SystemProperties.SequenceNumber, messageBody);

            // 用using管理SQL连接,自动释放资源,避免连接池耗尽
            using (var connection = new SqlConnection(_sqlConnStr))
            {
                await connection.OpenAsync(token);
                string query = "INSERT INTO [SalesLT].[messages] (message) VALUES(@Message)";
                using (var cmd = new SqlCommand(query, connection))
                {
                    cmd.Parameters.AddWithValue("@Message", messageBody);
                    await cmd.ExecuteNonQueryAsync(token);
                    _logger.LogInformation("Message saved to SQL successfully");
                }
            }

            // 标记消息已处理,避免重复接收
            await _queueClient.CompleteAsync(message.SystemProperties.LockToken);
        }

        // 处理ServiceBus异常
        private Task ExceptionHandler(ExceptionReceivedEventArgs args)
        {
            _logger.LogError(args.Exception, "ServiceBus message handler failed");
            var context = args.ExceptionReceivedContext;
            _logger.LogError("Exception context: Endpoint={ep}, Path={path}, Action={act}",
                context.Endpoint, context.EntityPath, context.Action);
            return Task.CompletedTask;
        }

        // 停止时清理ServiceBus客户端
        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("ServiceBus reader stopping...");
            await _queueClient.CloseAsync(cancellationToken);
            await base.StopAsync(cancellationToken);
        }
    }
}

2. 用Azure App Settings管理敏感配置,禁止硬编码

你代码里直接写死了ServiceBus连接字符串、SQL账号密码,这既不安全,也不便于在Azure环境中修改。应该把这些配置移到WebApp的应用设置和连接字符串中:

操作步骤:

  1. 打开Azure门户,找到你的WebApp
  2. 进入配置 > 应用设置,添加两个键值对:
    • ServiceBusConnectionString:值填你的ServiceBus完整连接字符串
    • QueueName:值填BasicQueue
  3. 进入配置 > 连接字符串,添加SQL连接:
    • 名称:SqlDatabase
    • 值:你的Azure SQL连接字符串(示例:Server=tcp:xxxx.database.windows.net,1433;Initial Catalog=mySampleDatabase;User ID=USERID;Password=PASSWORD;Encrypt=True;Connection Timeout=30;)
    • 类型选择SQLAzure

本地开发时,可以在appsettings.json中写占位符,Azure配置会自动覆盖本地值:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  },
  "ServiceBusConnectionString": "",
  "QueueName": "BasicQueue",
  "ConnectionStrings": {
    "SqlDatabase": ""
  }
}

3. 修复SQL连接的生命周期问题

之前用静态SqlConnection长期保持打开状态,容易导致连接池耗尽、连接失效。正确的做法是每次操作数据库时创建新连接,用using语句自动释放,这样能利用ADO.NET的连接池机制,避免资源泄漏。

上面的Worker示例中已经用了using (var connection = new SqlConnection(_sqlConnStr)),这就是标准的正确写法。


4. 启用WebApp日志,方便排查问题

WebApp环境中Console.WriteLine的输出不会直接显示,需要启用应用日志来查看运行状态和错误:

操作步骤:

  1. 进入WebApp的监控 > 日志流
  2. 点击开启,选择日志级别(比如Information)
  3. 之后你的ILogger日志会在这里实时展示,能快速定位ServiceBus或SQL的错误

5. 检查网络权限配置

如果你的Azure SQL或ServiceBus设置了网络防火墙,需要确保WebApp能访问它们:

  • Azure SQL:进入SQL服务器的防火墙和虚拟网络,勾选「允许Azure服务和资源访问此服务器」
  • ServiceBus:如果启用了VNet隔离,需要将WebApp的VNet加入ServiceBus的允许列表,或者开启「允许受信任的Microsoft服务绕过此防火墙」

完成这些调整后,重新部署应用到Azure WebApp,应该就能正常读取ServiceBus消息并写入SQL数据库了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 11:07:48