BackgroundService 内存泄漏 SqlDependency

BackgroundService memory leak SqlDependency

这是我的服务代码

public class Worker : BackgroundService
{
    public bool isRegister { get; set; }
    public bool checkIp { get; set; }
    public long timePass { get; set; }

    public Worker()
    {
    }

    public override Task StartAsync(CancellationToken cancellationToken)
    {
        return base.StartAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            if (isRegister == false)
                registerSelect();
            if (checkIp == true)
            {
                checkIp = false;
                await SecurityConfig.cacheServices?.BuildServiceProvider()?.GetService<IBlockFirewallIpService>().RegisterInFirewall();
            }
            timePass += 1000;
            if (timePass % 60000 == 0)
                await SecurityConfig.cacheServices?.BuildServiceProvider()?.GetService<IBlockFirewallIpService>().RegisterInFirewall();

            await Task.Delay(1000, stoppingToken);
        }
    }

    public void registerSelect()
    {
        isRegister = true;
        using (SqlConnection conn = new SqlConnection(GetDbConnection()))
        {
            conn.Open();

            SqlDependency.Start(GetDbConnection());

            string commandText = "SELECT [Ip1],[Ip2] ,[Ip3] ,[Ip4] FROM dbo.BlockFirewallIps where IsRead is null";

            using (SqlCommand cmd = new SqlCommand(commandText, conn))
            {
                SqlDependency dependency = new SqlDependency(cmd);

                dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);

                cmd.ExecuteNonQuery();
            }
            conn.Close();
        }
    }

    void OnDependencyChange(object sender, SqlNotificationEventArgs e)
    {
        if (e.Info == SqlNotificationInfo.Insert)
            checkIp = true;
        SqlDependency temp = sender as SqlDependency;
        if (temp != null)
            temp.OnChange -= new OnChangeEventHandler(OnDependencyChange);

        registerSelect();
    }

    private string GetDbConnection()
    {
        return GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"];
    }
}

这是我的 IBlockFirewallIpService.RegisterInFirewall() 代码

 public async Task RegisterInFirewall()
    {
        var allBlockIps = await db.BlockFirewallIps.Where(t => t.IsRead == null).ToListAsync();
        foreach (var ip in allBlockIps)
        {
            BanIP("OjeFirTCP" + ip.Ip1 + "_" + ip.Ip2 + "_" + ip.Ip3 + "_" + ip.Ip4, ip.Ip1 + "." + ip.Ip2 + "." + ip.Ip3 + "." + ip.Ip4, "Any", "TCP");
            BanIP("OjeFirUDP" + ip.Ip1 + "_" + ip.Ip2 + "_" + ip.Ip3 + "_" + ip.Ip4, ip.Ip1 + "." + ip.Ip2 + "." + ip.Ip3 + "." + ip.Ip4, "Any", "UDP");
            ip.IsRead = true;
            db.SaveChanges();
        }
    }

    void BanIP(string RuleName, string IPAddress, string Port, string Protocol)
    {
        if (OperatingSystem.IsWindows())
        {
            if (!string.IsNullOrEmpty(RuleName) && !string.IsNullOrEmpty(IPAddress) && !string.IsNullOrEmpty(Port) && !string.IsNullOrEmpty(Protocol) && new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator))
            {
                using (Process RunCmd = new Process())
                {
                    RunCmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    RunCmd.StartInfo.FileName = "cmd.exe";
                    RunCmd.StartInfo.Arguments = "/C netsh advfirewall firewall add rule name=\"" + RuleName + "\" dir=in action=block remoteip=" + IPAddress + " remoteport=" + Port + " protocol=" + Protocol;
                    RunCmd.Start();
                }
            }
        }
    }

这是progeram.cs

IHost host = Host.CreateDefaultBuilder(args)
 .UseWindowsService(options =>
 {
     options.ServiceName = "OjeFirewall";
 })
.ConfigureServices((hostContext, services) =>
{
    GlobalConfig.Configuration = hostContext.Configuration;
    services.AddScoped<IHttpContextAccessor, FakeIHttpContextAccessor>();
    SecurityConfig.Config(services);
    services.AddHostedService<Worker>();
})
.Build();

等待host.RunAsync();

这是 SecurityConfig.Config 个代码

 services.AddDbContext<SecurityDBContext>(options =>
                options.UseSqlServer(GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"],
                b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
                , ServiceLifetime.Singleton
        );

        services.AddSingleton<IIpLimitationWhiteListService, IpLimitationWhiteListService>();
        services.AddSingleton<IIpLimitationBlackListService, IpLimitationBlackListService>();
        services.AddSingleton<IFileAccessRoleService, FileAccessRoleService>();
        services.AddSingleton<IRoleService, RoleService>();
        services.AddSingleton<IBlockClientConfigService, BlockClientConfigService>();
        services.AddSingleton<IBlockAutoIpService, BlockAutoIpService>();
        services.AddSingleton<IBlockFirewallIpService, BlockFirewallIpService>();

问题:

此代码在 3 天后使用了太多内存 第一次调用(OnDependencyChange)后,启动 ram 使用量为 20mb,它使用 47mb 3 天后使用 178mb

我哪里做错了?!

我发现了问题

await SecurityConfig.cacheServices?.BuildServiceProvider()?.GetService<IBlockFirewallIpService>().RegisterInFirewall();

这行代码是改成正常注入ram后的问题,现在使用稳定,但我不明白为什么!?