EntityFrameworkCore+MySQL在Ubuntu/Linux下Min Pool Size>0时挂起
问题:.NET + MySQL连接池最小配置在Ubuntu环境下挂起
我使用.NET 8.0(也测试过.NET 6.0),搭配最新版EntityFrameworkCore、Pomelo.EntityFrameworkCore.MySql(也试过MySql.EntityFrameworkCore),MySQL版本为8.0.35(Docker部署和主机安装版都测过)。连接字符串里配置了Min Pool Size=10,应用在各类Windows环境(本地机器、Windows上的Ubuntu虚拟机、Windows上的Docker Compose、Azure平台)运行正常,但在Ubuntu 22.04物理机或Ubuntu 22.04上的Docker Compose环境中,建立数据库连接时会挂起。
移除Min Pool Size=10后,所有环境都能正常运行。哪怕延长连接超时时间,只要Min Pool Size>0,应用依然卡顿。
抛出的异常
app | Unhandled exception. System.AggregateException: One or more errors occurred. (Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.) app | ---> MySql.Data.MySqlClient.MySqlException (0x80004005): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. app | ---> System.TimeoutException: The operation has timed out. app | at MySql.Data.Common.StreamCreator.<>c.<GetTcpStreamAsync>b__8_1() app | at System.Threading.CancellationTokenSource.Invoke(Delegate d, Object state, CancellationTokenSource source) app | at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) app | --- End of stack trace from previous location --- app | at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) app | at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException) app | --- End of inner exception stack trace --- app | at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException) app | at System.Threading.TimerQueueTimer.Fire(Boolean isThreadPool) app | at System.Threading.TimerQueue.FireNextTimers() app | at System.Threading.ThreadPoolWorkQueue.Dispatch() app | at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
复现代码
原始应用是ASP.NET Core API,已通过简易控制台应用复现问题:
主程序(Program.cs)
static async Task Main(string[] args) { var envCnx = Environment.GetEnvironmentVariable("CONNECTION_STRING"); IDbContextFactory dbContextFactory = new AppDbContextFactory(envCnx); using var db = await dbContextFactory.CreateDbContextAsync(); await db.Database.MigrateAsync(); var name = "User 1"; if (!await db.Set<UserInfo>().AnyAsync(x => x.Name == name)) { db.Add(new UserInfo() { Id = Guid.NewGuid(), Name = name, CreatedOn = DateTime.Now }); await db.SaveChangesAsync(); } }
AppDbContextFactory
public interface IDbContextFactory : IDbContextFactory<DbContext> { } public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>, IDbContextFactory<AppDbContext>, IDbContextFactory { private readonly string _cnx; public AppDbContextFactory(string cnx) { _cnx = cnx; } public AppDbContext CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); string cnx = args.Length > 0 ? args[0] : this._cnx; #if DEBUG optionsBuilder.EnableSensitiveDataLogging(); optionsBuilder.EnableDetailedErrors(); #endif optionsBuilder.UseMySql(cnx, ServerVersion.AutoDetect(cnx), mySqlOpts => { mySqlOpts.UseNewtonsoftJson(); mySqlOpts.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName); }) #if DEBUG .EnableSensitiveDataLogging() .EnableDetailedErrors() #endif ; return new AppDbContext(optionsBuilder.Options); } public AppDbContext GetContext() { return CreateDbContext([_cnx]); } public AppDbContext CreateDbContext() { return GetContext(); } DbContext IDbContextFactory<DbContext>.CreateDbContext() { return GetContext(); } }
AppDbContext
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<UserInfo>().HasKey(x => x.Id); modelBuilder.Entity<UserInfo>().HasIndex(x => x.Id).IsUnique(); modelBuilder.Entity<UserInfo>().Property(t => t.Id).ValueGeneratedOnAdd(); modelBuilder.Entity<UserInfo>().Property(x => x.Timestamp).IsRowVersion(); modelBuilder.Entity<UserInfo>().HasIndex(x => x.Name); modelBuilder.Entity<UserInfo>().ToTable("users"); } }
UserInfo实体
public class UserInfo { [JsonConstructor] public UserInfo() { } public Guid Id { get; set; } public string Name { get; set; } public DateTimeOffset CreatedOn { get; set; } public DateTime Timestamp { get; set; } }
Docker配置
Dockerfile
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base USER app WORKDIR /app FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY ["ConsoleTestApp/ConsoleTestApp.csproj", "ConsoleTestApp/"] RUN dotnet restore "./ConsoleTestApp/ConsoleTestApp.csproj" COPY . . WORKDIR "/src/ConsoleTestApp" RUN dotnet build "./ConsoleTestApp.csproj" -c $BUILD_CONFIGURATION -o /app/build FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "./ConsoleTestApp.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "ConsoleTestApp.dll"]
docker-compose.yml
version: "3.3" name: "test-app" services: app: image: console-app:latest container_name: console-app depends_on: - mysql environment: # CONNECTION_STRING: server=mysql,3306;user=root;password=2YSCK+fktPRGOi5omfRjCAP_am9+3r;database=test_db CONNECTION_STRING: server=mysql,3306;user=root;password=2YSCK+fktPRGOi5omfRjCAP_am9+3r;database=test_db;Min Pool Size=10;Connect Timeout=600 mysql: container_name: db image: mysql:8.0.35 cap_add: - SYS_NICE restart: always ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: 2YSCK+fktPRGOi5omfRjCAP_am9+3r MYSQL_DATABASE: test_db
项目结构
. ├── ConsoleTestApp │ ├── ConsoleTestApp.csproj │ ├── Data │ │ ├── AppDbContext.cs │ │ ├── AppDbContextFactory.cs │ │ ├── Migrations │ │ │ ├── 20240523164903_InitialMigration.Designer.cs │ │ │ ├── 20240523164903_InitialMigration.cs │ │ │ └── AppDbContextModelSnapshot.cs │ │ └── UserInfo.cs │ ├── Program.cs │ └── Properties │ └── launchSettings.json ├── ConsoleTestApp.sln ├── Dockerfile └── docker-compose.yml
内容的提问来源于stack exchange,提问作者jjavierdguezas
相关产品推荐
相关产品推荐

