.NET Core迁移至.NET 6后,AWS Serverless应用无法连接MySQL主机
.NET 6 Lambda 无法连接 AWS RDS MySQL 主机问题排查
问题概述
项目从.NET Core 3.0迁移至.NET 6后,部署到AWS Serverless(Lambda)的.NET 6 API无法连接MySQL主机,Swagger和Postman请求均返回504网关超时,但本地运行完全正常。
环境信息
- ORM框架:Entity Framework Core Code First
- MySQL驱动:Pomelo.EntityFrameworkCore.MySql 6.0.1
- EF Core版本:Microsoft.EntityFrameworkCore 6.0.2
- 部署环境:AWS Lambda(.NET 6运行时),已按官方文档完成基础配置
错误日志
[Error] Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction: Unknown error responding to request: MySqlException: MySqlConnector.MySqlException (0x80004005): Unable to connect to any of the specified MySQL hosts. at NurseScalettAPI.Controllers.UserManagementController.GetCaseDetails(Int32 caseId) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task last task, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction.ProcessRequest(ILambdaContext lambdaContext, Object context, InvokeFeatures features, Boolean rethrowUnhandledError)
已尝试操作
- 移除连接字符串中的冗余空格和重复配置,当前连接字符串:
"DefaultConnection": "server=aws-cluster-instance-1.cva0y.us-east-1.rds.amazonaws.com;port=3306;database=sample_database;user=sample_user;password=passwrd;Persist Security Info=false;Connect Timeout=60;Convert Zero Datetime=true;Allow User Variables=True;"
核心代码片段
DataContext 代码
public class DataContext: DbContext { protected readonly IConfiguration Configuration; private readonly AppSettingOptions _options; public DataContext(IConfiguration configuration, IOptions<AppSettingOptions> options) { Configuration = configuration; _options = options.Value; } protected override void OnConfiguring(DbContextOptionsBuilder options) { string mySqlConnectionString = _options.DefaultConnection; if (mySqlConnectionString != null) { options.UseMySql(mySqlConnectionString, ServerVersion.AutoDetect(mySqlConnectionString)); } } public DbSet<CaseDetails> CaseDetails { get; set; } }
UserManagementController 代码
[Route("api/[controller]")] [ApiController] public class UserManagementController : ControllerBase { private readonly ICoreLogger<UserManagementController> _logger; private readonly DataContext _context; private readonly IUserRepository _userService; private readonly IHttpContextAccessor _httpContext; private readonly IConfiguration _config; public UserManagementController(IUserRepository userService, IPersistService cache, DataContext context, IHttpContextAccessor httpContext, ICoreLogger<UserManagementController> logger, IConfiguration config) { _logger = logger; _context = context; _userService = userService; _httpContext = httpContext; _config = config; } [HttpGet("{caseId}")] public async Task<IActionResult> GetCaseDetails(int caseId) { try { var connStr1 = _config.GetConnectionString("DefaultConnection"); _logger.LogInformation($"Get connectionstring from appsetting : {connStr1}"); // 可在CloudWatch日志中打印 var str = _context.Database.GetConnectionString().Split(";"); _logger.LogInformation($"Connection string: {str[0]} , {str[2]}"); var result = _context.CaseDetails.FirstOrDefault(x => x.Case_Id == caseId); return result != null ? Ok(result) : BadRequest(); } catch (Exception ex) { throw ex; } } }
Startup.cs 代码
public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); builder.AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.Configure<AppSettingOptions>(Configuration.GetSection(AppSettingOptions.ConnectionStrings)); services.AddDbContext<DataContext>(); services.AddSingleton<IServiceCollectionProvider>(new ServiceCollectionProvider(services)); services.AddControllers(); services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); #region UserManagement services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => false; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMemoryCache(); services.AddHttpContextAccessor(); services.Configure<AppConfig>(Configuration.GetSection("AppConfig")); services.Configure<S3Configuration>(Configuration.GetSection("AwsConfiguration")); services.Configure<SearchConfig>(Configuration.GetSection("SearchConfig")); services.Configure<ExternalKnowledgeSource>(Configuration.GetSection("ExternalKnowledgeSource")); services.Configure<SQSConfiguration>(Configuration.GetSection("ServiceConfiguration")); services.AddScoped<IUserRepository, UserRepository>(); services.AddScoped<UserContextManager>(); services.AddScoped<IPersistService, CacheService>(); #endregion services.AddScoped<IUserService, UserServices>(); services.AddScoped<IUserServiceAsync, UserServicesAsync>(); services.AddSingleton<IAppConfiguration, AppConfiguration>(); services.AddSingleton(typeof(ICoreLogger<>), typeof(CoreLogger<>)); #region UserManagement services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.LoginPath = "/User/Login"; options.LogoutPath = "/User/Logout"; options.ReturnUrlParameter = ""; }); #endregion services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "Nurse Scarlett API", Description = "Nurse Scarlett API" }); }); services.AddAWSLambdaHosting(LambdaEventSource.RestApi); services.AddCors(options => { options.AddPolicy( name: "AllowOrigin", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseCors("AllowOrigin"); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseSwagger(); app.UseSwaggerUI(c => { var swaggerJsonBasePath = string.IsNullOrWhiteSpace(c.RoutePrefix) ? "." : ".."; c.SwaggerEndpoint($"{swaggerJsonBasePath}/swagger/v1/swagger.json", "MyAPI"); }); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Welcome to running ASP.NET Core on AWS Lambda"); }); }); } }
排查建议
网络连通性:
- 检查Lambda所在VPC的安全组是否允许出站访问3306端口
- 确认RDS安全组是否开放Lambda所在VPC/安全组的入站连接
- 若Lambda在私有子网,需配置NAT网关或RDS VPC端点以实现内网访问
连接字符串优化:
- 确认Lambda环境变量中是否正确配置连接字符串(AWS Lambda优先读取环境变量,而非仅appsettings)
- 添加
SslMode=Required参数(AWS RDS默认强制SSL连接) - 简化连接字符串至必要参数:
server=xxx;port=3306;database=xxx;user=xxx;password=xxx;SslMode=Required
EF Core配置调整:
- 替换
ServerVersion.AutoDetect为手动指定版本,例如ServerVersion.Parse("8.0.28-mysql"),避免Lambda环境中版本检测失败 - 验证
AppSettingOptions.ConnectionStrings节点是否与appsettings中的配置键名完全匹配
- 替换
Lambda超时设置:
- 当前连接超时为60秒,需将Lambda函数的超时时间设置为大于60秒(如90秒),避免因连接未完成导致的504超时
依赖包更新:
- 升级Pomelo.EntityFrameworkCore.MySql至最新兼容.NET 6的版本,修复潜在的版本兼容性问题
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

