diff --git a/src/FastGateway.Service/BackgroundTask/ClientRequestBackgroundTask.cs b/src/FastGateway.Service/BackgroundTask/ClientRequestBackgroundTask.cs index 8347bf7..08a147a 100644 --- a/src/FastGateway.Service/BackgroundTask/ClientRequestBackgroundTask.cs +++ b/src/FastGateway.Service/BackgroundTask/ClientRequestBackgroundTask.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Threading.Channels; using FastGateway.Entities; using FastGateway.Service.DataAccess; using FastGateway.Service.Dto; @@ -10,47 +9,40 @@ namespace FastGateway.Service.BackgroundTask; public class ClientRequestBackgroundTask(IServiceProvider serviceProvider, ISearcher searcher) : BackgroundService { - private static Channel ClientRequestChannel { get; } = - Channel.CreateUnbounded(); - /// /// 线程安全集合 /// - private readonly ConcurrentBag _loggerBag = new(); + private static readonly ConcurrentBag LoggerBag = new(); public static void AddLogger(ClientRequestLoggerInput input) { - ClientRequestChannel.Writer.TryWrite(input); + LoggerBag.Add(input); } - protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 暂停1分钟 await Task.Delay(1000 * 60, stoppingToken); - _ = Start(stoppingToken); await RunLoggerSave(stoppingToken); } private async Task RunLoggerSave(CancellationToken stoppingToken) { await using var scope = serviceProvider.CreateAsyncScope(); - var masterContext = scope.ServiceProvider.GetRequiredService(); var loggerContext = scope.ServiceProvider.GetRequiredService(); var logger = scope.ServiceProvider.GetRequiredService>(); while (!stoppingToken.IsCancellationRequested) { - if (_loggerBag.IsEmpty) + if (LoggerBag.IsEmpty) { - // 等待5s await Task.Delay(10000, stoppingToken); continue; } var loggerList = new List(); - while (_loggerBag.TryTake(out var loggerItem)) + while (LoggerBag.TryTake(out var loggerItem)) { loggerList.Add(loggerItem); } @@ -126,7 +118,7 @@ public class ClientRequestBackgroundTask(IServiceProvider serviceProvider, ISear foreach (var item in updateLoggerList) { - await loggerContext.ClientRequestLoggers.Where(x => x.RequestTime == now && x.Ip == item.Ip) + await loggerContext.ClientRequestLoggers.Where(x => x.Ip == item.Ip && x.RequestTime == now) .ExecuteUpdateAsync(x => x.SetProperty(a => a.Total, a => a.Total + item.Total) .SetProperty(a => a.Success, a => a.Success + item.Success) .SetProperty(a => a.Fail, a => a.Fail), @@ -143,12 +135,4 @@ public class ClientRequestBackgroundTask(IServiceProvider serviceProvider, ISear } } } - - private async Task Start(CancellationToken stoppingToken) - { - await foreach (var item in ClientRequestChannel.Reader.ReadAllAsync(stoppingToken)) - { - _loggerBag.Add(item); - } - } } \ No newline at end of file diff --git a/src/FastGateway.Service/BackgroundTask/LoggerBackgroundTask.cs b/src/FastGateway.Service/BackgroundTask/LoggerBackgroundTask.cs index b33acab..105891b 100644 --- a/src/FastGateway.Service/BackgroundTask/LoggerBackgroundTask.cs +++ b/src/FastGateway.Service/BackgroundTask/LoggerBackgroundTask.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Threading.Channels; +using System.Threading.Channels; using FastGateway.Entities; using FastGateway.Service.DataAccess; using IP2Region.Net.Abstractions; @@ -8,52 +7,36 @@ namespace FastGateway.Service.BackgroundTask; public sealed class LoggerBackgroundTask(IServiceProvider serviceProvider, ISearcher searcher) : BackgroundService { - private static Channel LoggerChannel { get; } = Channel.CreateUnbounded(); - - public static void AddLogger(ApplicationLogger logger) - { - LoggerChannel.Writer.TryWrite(logger); - } - /// /// 线程安全集合 /// - private readonly ConcurrentBag _loggerBag = new(); + private static readonly Channel LoggerBag = Channel.CreateUnbounded(); + + public static void AddLogger(ApplicationLogger logger) + { + LoggerBag.Writer.TryWrite(logger); + } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - // 暂停1分钟 - _ = Start(stoppingToken); await RunLoggerSave(stoppingToken); } private async Task RunLoggerSave(CancellationToken stoppingToken) { await using var scope = serviceProvider.CreateAsyncScope(); - var masterContext = scope.ServiceProvider.GetRequiredService(); - var loggerContext = scope.ServiceProvider.GetRequiredService(); - var logger = scope.ServiceProvider.GetRequiredService>(); + await using var loggerContext = scope.ServiceProvider.GetRequiredService(); while (!stoppingToken.IsCancellationRequested) { - if (_loggerBag.IsEmpty) - { - // 等待5s - await Task.Delay(5000, stoppingToken); - continue; - } - - var loggerList = new List(); - while (_loggerBag.TryTake(out var loggerItem)) - { - loggerList.Add(loggerItem); - } - try { - foreach (var item in loggerList) + int count = 0; + while (LoggerBag.Reader.TryRead(out var item)) { - if (string.IsNullOrWhiteSpace(item.Ip)) continue; + if (string.IsNullOrWhiteSpace(item.Ip)) + continue; + var locations = searcher.Search(item.Ip)?.Split("|", StringSplitOptions.RemoveEmptyEntries); locations = locations?.Where(x => x != "0").ToArray(); @@ -69,23 +52,20 @@ public sealed class LoggerBackgroundTask(IServiceProvider serviceProvider, ISear .Replace("移动", "") .TrimStart('|') .TrimEnd('|'); - } - await loggerContext.ApplicationLoggers.AddRangeAsync(loggerList, stoppingToken); - await loggerContext.SaveChangesAsync(stoppingToken); + await loggerContext.ApplicationLoggers.AddAsync(item, stoppingToken); + count++; + if (count >= 100) + { + await loggerContext.SaveChangesAsync(stoppingToken); + } + } } catch (Exception e) { - logger.LogError(e, "日志保存失败"); + Console.WriteLine(e); + await Task.Delay(500, stoppingToken); } } } - - private async Task Start(CancellationToken stoppingToken) - { - await foreach (var item in LoggerChannel.Reader.ReadAllAsync(stoppingToken)) - { - _loggerBag.Add(item); - } - } } \ No newline at end of file diff --git a/src/FastGateway.Service/BackgroundTask/RenewSSLBackgroundService.cs b/src/FastGateway.Service/BackgroundTask/RenewSSLBackgroundService.cs index cfd49d7..c0fcfb2 100644 --- a/src/FastGateway.Service/BackgroundTask/RenewSSLBackgroundService.cs +++ b/src/FastGateway.Service/BackgroundTask/RenewSSLBackgroundService.cs @@ -10,7 +10,7 @@ public class RenewSSLBackgroundService(ILogger logger protected override async Task ExecuteAsync(CancellationToken stoppingToken) { logger.LogInformation("证书自动续期服务已启动"); - + // 暂停1分钟 await Task.Delay(1000 * 60, stoppingToken); @@ -27,6 +27,8 @@ public class RenewSSLBackgroundService(ILogger logger .Where(x => x.NotAfter == null || x.NotAfter < DateTime.Now.AddDays(15) && x.AutoRenew) .ToArrayAsync(stoppingToken); + var isRenew = false; + foreach (var cert in certs) { try @@ -43,6 +45,8 @@ public class RenewSSLBackgroundService(ILogger logger .SetProperty(y => y.Certs, cert.Certs), stoppingToken); logger.LogInformation($"证书续期成功:{cert.Id} {cert.Domain}"); + + isRenew = true; } catch (Exception e) { @@ -57,7 +61,10 @@ public class RenewSSLBackgroundService(ILogger logger } } - CertService.InitCert(await dbContext.Certs.ToListAsync(cancellationToken: stoppingToken)); + if (isRenew) + { + CertService.InitCert(await dbContext.Certs.ToArrayAsync(cancellationToken: stoppingToken)); + } // 每24小时检查一次 await Task.Delay(1000 * 60 * 60 * 24, stoppingToken); diff --git a/src/FastGateway.Service/Gateway/Gateway.cs b/src/FastGateway.Service/Gateway/Gateway.cs index 3eca08b..8dad808 100644 --- a/src/FastGateway.Service/Gateway/Gateway.cs +++ b/src/FastGateway.Service/Gateway/Gateway.cs @@ -47,7 +47,7 @@ public static class Gateway /// /// /// - public static void ReloadGateway(Server server, List domainNames) + public static void ReloadGateway(Server server, DomainName[] domainNames) { if (GatewayWebApplications.TryGetValue(server.Id, out var webApplication)) { @@ -107,8 +107,8 @@ public static class Gateway /// /// 构建网关 /// - public static async Task BuilderGateway(Server server, List domainNames, - List blacklistAndWhitelists, List rateLimits) + public static async Task BuilderGateway(Server server, DomainName[] domainNames, + BlacklistAndWhitelist[] blacklistAndWhitelists, RateLimit[] rateLimits) { try { @@ -339,12 +339,11 @@ public static class Gateway } private static (IReadOnlyList routes, - IReadOnlyList clusters) BuildConfig(List domainNames) + IReadOnlyList clusters) BuildConfig(DomainName[] domainNames) { var routes = new List(); var clusters = new List(); - foreach (var domainName in domainNames) { if (string.IsNullOrWhiteSpace(domainName.Path)) diff --git a/src/FastGateway.Service/Infrastructure/ISystemUsage.cs b/src/FastGateway.Service/Infrastructure/ISystemUsage.cs index 4cd5b3f..78b68a3 100644 --- a/src/FastGateway.Service/Infrastructure/ISystemUsage.cs +++ b/src/FastGateway.Service/Infrastructure/ISystemUsage.cs @@ -130,41 +130,47 @@ public class LinuxSystemUsage : ISystemUsage } public (float memoryUsage, ulong totalMemory, ulong useMemory) GetMemoryUsage() - { - var memInfo = ExecuteCommand("cat /proc/meminfo", "/bin/bash"); - var lines = memInfo.Split('\n'); - ulong totalMemory = 0; - ulong availableMemory = 0; + { + var memInfo = ExecuteCommand("cat /proc/meminfo", "/bin/bash"); + var lines = memInfo.Split('\n'); + ulong totalMemory = 0; + ulong availableMemory = 0; - foreach (var line in lines) - { - var parts = line.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length < 2) continue; + foreach (var line in lines) + { + var parts = line.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length < 2) continue; - var key = parts[0].Trim(); - var value = ulong.Parse(parts[1].Trim().Split(' ')[0]); + var key = parts[0].Trim(); + var value = ulong.Parse(parts[1].Trim().Split(' ')[0]); - switch (key) - { - case "MemTotal": - totalMemory = value; - break; - case "MemAvailable": - availableMemory = value; - break; - } - } + switch (key) + { + case "MemTotal": + totalMemory = value; + break; + case "MemAvailable": + availableMemory = value; + break; + } + } - // 默认单位是KB,需要转换byte - totalMemory *= 1024; - availableMemory *= 1024; + // 默认单位是KB,需要转换byte + totalMemory *= 1024; + availableMemory *= 1024; - ulong usedMemory = totalMemory - availableMemory; - float memoryUsage = (float)usedMemory / totalMemory * 100; + ulong usedMemory = totalMemory - availableMemory; + float memoryUsage = (float)usedMemory / totalMemory * 100; - return (memoryUsage, totalMemory, usedMemory); - } + return (memoryUsage, totalMemory, usedMemory); + } + + private long previousReads = 0; + private long previousSectorsRead = 0; + private long previousWrites = 0; + private long previousSectorsWritten = 0; + public (float read, float write) GetDiskUsage() { @@ -178,8 +184,24 @@ public class LinuxSystemUsage : ISystemUsage var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length > 13) { - readBytes += float.Parse(parts[5]) * 512 / 1024; // sectors read * 512 bytes per sector to KB - writeBytes += float.Parse(parts[9]) * 512 / 1024; // sectors written * 512 bytes per sector to KB + string deviceName = parts[2]; // 设备名,通常是第三列 + long readsCompleted = long.Parse(parts[3]); // 完成的读操作 + long sectorsRead = long.Parse(parts[5]); // 读取的扇区数 + long writesCompleted = long.Parse(parts[7]); // 完成的写操作 + long sectorsWritten = long.Parse(parts[9]); // 写入的扇区数 + long byteRead = (sectorsRead - previousSectorsRead) * 512; // 每个扇区512字节 + long byteWritten = (sectorsWritten - previousSectorsWritten) * 512; + + previousReads = readsCompleted; + previousSectorsRead = sectorsRead; + previousWrites = writesCompleted; + previousSectorsWritten = sectorsWritten; + + if (deviceName == "sda") + { + readBytes += byteRead; + writeBytes += byteWritten; + } } } diff --git a/src/FastGateway.Service/Program.cs b/src/FastGateway.Service/Program.cs index 22b1f91..29f9c68 100644 --- a/src/FastGateway.Service/Program.cs +++ b/src/FastGateway.Service/Program.cs @@ -76,6 +76,7 @@ public static class Program builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); + builder.Services.AddHostedService(); builder.Services.AddDbContext(optionsBuilder => { // 判断当前目录是否存在data文件夹 @@ -109,18 +110,15 @@ public static class Program await dbContext.Database.MigrateAsync(); await loggerContext.Database.MigrateAsync(); - var certs = await dbContext.Certs.Where(x => x.Expired == false).ToListAsync(); + CertService.InitCert(await dbContext.Certs.ToArrayAsync()); - CertService.InitCert(certs); - - var server = await dbContext.Servers.ToListAsync(); - var domainNames = await dbContext.DomainNames.ToListAsync(); - var blacklistAndWhitelists = await dbContext.BlacklistAndWhitelists.ToListAsync(); - var rateLimits = await dbContext.RateLimits.ToListAsync(); - foreach (var item in server) + var domainNames = await dbContext.DomainNames.ToArrayAsync(); + var blacklistAndWhitelists = await dbContext.BlacklistAndWhitelists.ToArrayAsync(); + var rateLimits = await dbContext.RateLimits.ToArrayAsync(); + foreach (var item in await dbContext.Servers.ToArrayAsync()) { await Task.Factory.StartNew(async () => - await Gateway.Gateway.BuilderGateway(item, domainNames.Where(x => x.ServerId == item.Id).ToList(), + await Gateway.Gateway.BuilderGateway(item, domainNames.Where(x => x.ServerId == item.Id).ToArray(), blacklistAndWhitelists, rateLimits)); } } diff --git a/src/FastGateway.Service/Services/BlacklistAndWhitelistService.cs b/src/FastGateway.Service/Services/BlacklistAndWhitelistService.cs index 957ad59..212d6bc 100644 --- a/src/FastGateway.Service/Services/BlacklistAndWhitelistService.cs +++ b/src/FastGateway.Service/Services/BlacklistAndWhitelistService.cs @@ -10,7 +10,7 @@ namespace FastGateway.Service.Services; public static class BlacklistAndWhitelistService { public static WebApplication UseBlacklistMiddleware(this WebApplication app, - List blacklistAndWhitelists) + BlacklistAndWhitelist[] blacklistAndWhitelists) { var whitelist = blacklistAndWhitelists.Where(x => x.Enable && !x.IsBlacklist).ToArray(); diff --git a/src/FastGateway.Service/Services/CertService.cs b/src/FastGateway.Service/Services/CertService.cs index 492f650..67e2ba8 100644 --- a/src/FastGateway.Service/Services/CertService.cs +++ b/src/FastGateway.Service/Services/CertService.cs @@ -43,7 +43,7 @@ public static class CertService return CertWebApplications.TryGetValue(domain, out var cert) ? cert : null; } - public static void InitCert(List certs) + public static void InitCert(Cert[] certs) { foreach (var cert in certs) { @@ -111,7 +111,7 @@ public static class CertService .SetProperty(x => x.RenewTime, x => cert.RenewTime) ); - InitCert(await masterContext.Certs.Where(x => !x.Expired).ToListAsync()); + InitCert(await masterContext.Certs.ToArrayAsync()); } return ResultDto.CreateSuccess(); diff --git a/src/FastGateway.Service/Services/QpsService.cs b/src/FastGateway.Service/Services/QpsService.cs index 7ee8956..620cead 100644 --- a/src/FastGateway.Service/Services/QpsService.cs +++ b/src/FastGateway.Service/Services/QpsService.cs @@ -31,7 +31,7 @@ public sealed class QpsItem public static class QpsService { - private static readonly QpsItem qpsItem = new(); + private static readonly QpsItem QpsItem = new(); private static bool _enable; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -42,7 +42,7 @@ public static class QpsService return; } - qpsItem.IncrementRequests(); + QpsItem.IncrementRequests(); } public static void CalculateServiceQps() @@ -52,14 +52,14 @@ public static class QpsService return; } - qpsItem.CalculateQps(); + QpsItem.CalculateQps(); } public static int GetServiceQps() { - var qps = qpsItem.GetQps(); + var qps = QpsItem.GetQps(); - qpsItem.CalculateQps(); + QpsItem.CalculateQps(); return qps; } diff --git a/src/FastGateway.Service/Services/RateLimitService.cs b/src/FastGateway.Service/Services/RateLimitService.cs index dde4d0c..cc00cda 100644 --- a/src/FastGateway.Service/Services/RateLimitService.cs +++ b/src/FastGateway.Service/Services/RateLimitService.cs @@ -10,10 +10,10 @@ namespace FastGateway.Service.Services; public static class RateLimitService { - public static IServiceCollection AddRateLimitService(this IServiceCollection services, List rateLimits) + public static IServiceCollection AddRateLimitService(this IServiceCollection services, RateLimit[] rateLimits) { // 如果启用限流则添加限流中间件 - if (rateLimits.Count != 0) + if (rateLimits.Length != 0) { services.AddMemoryCache(); services.Configure @@ -48,9 +48,9 @@ public static class RateLimitService return services; } - public static IEndpointRouteBuilder UseRateLimitMiddleware(this WebApplication app, List rateLimits) + public static IEndpointRouteBuilder UseRateLimitMiddleware(this WebApplication app, RateLimit[] rateLimits) { - if (rateLimits.Count == 0) + if (rateLimits.Length == 0) { return app; } @@ -69,33 +69,31 @@ public static class RateLimitService .AddEndpointFilter() .WithDisplayName("限流"); - rateLimit.MapPost(string.Empty, async (MasterContext dbContext, RateLimit rateLimit) => + rateLimit.MapPost(string.Empty, async (MasterContext dbContext, RateLimit limit) => { - if (string.IsNullOrWhiteSpace(rateLimit.Name)) + if (string.IsNullOrWhiteSpace(limit.Name)) { throw new ValidationException("限流名称不能为空"); } - if (await dbContext.RateLimits.AnyAsync(x => x.Name == rateLimit.Name)) + if (await dbContext.RateLimits.AnyAsync(x => x.Name == limit.Name)) { throw new ValidationException("限流名称已存在"); } - dbContext.RateLimits.Add(rateLimit); + dbContext.RateLimits.Add(limit); await dbContext.SaveChangesAsync(); }).WithDescription("创建限流").WithDisplayName("创建限流").WithTags("限流"); rateLimit.MapGet(string.Empty, async (MasterContext dbContext, int page, int pageSize) => { - var result = await dbContext.RateLimits - .Skip((page - 1) * pageSize) - .ToListAsync(); - var total = await dbContext.RateLimits.CountAsync(); - return new PagingDto(total, result); + return new PagingDto(total, await dbContext.RateLimits + .Skip((page - 1) * pageSize) + .ToListAsync()); }) .WithDescription("获取限流列表") .WithDisplayName("获取限流列表") diff --git a/src/FastGateway.Service/Services/ServerService.cs b/src/FastGateway.Service/Services/ServerService.cs index 66b17dd..dfed1f9 100644 --- a/src/FastGateway.Service/Services/ServerService.cs +++ b/src/FastGateway.Service/Services/ServerService.cs @@ -75,6 +75,7 @@ public static class ServerService dbContext.Servers.Update(server); await dbContext.SaveChangesAsync(); + }).WithDescription("更新服务").WithDisplayName("更新服务").WithTags("服务"); server.MapPut("{id}/enable", async (MasterContext dbContext, string id) => @@ -82,6 +83,7 @@ public static class ServerService await dbContext.Servers .Where(x => x.Id == id) .ExecuteUpdateAsync(i => i.SetProperty(a => a.Enable, a => !a.Enable)); + }).WithDescription("启用/禁用服务").WithDisplayName("启用/禁用服务").WithTags("服务"); // 启用服务 @@ -90,13 +92,22 @@ public static class ServerService if (!Gateway.Gateway.CheckServerOnline(id)) { var server = await dbContext.Servers.FirstOrDefaultAsync(x => x.Id == id); - var domainNames = await dbContext.DomainNames.Where(x => x.ServerId == id).ToListAsync(); - var blacklistAndWhitelists = await dbContext.BlacklistAndWhitelists.ToListAsync(); - var rateLimits = await dbContext.RateLimits.ToListAsync(); + var domainNames = await dbContext.DomainNames.Where(x => x.ServerId == id).ToArrayAsync(); + var blacklistAndWhitelists = await dbContext.BlacklistAndWhitelists.ToArrayAsync(); + var rateLimits = await dbContext.RateLimits.ToArrayAsync(); await Task.Factory.StartNew(async () => await Gateway.Gateway.BuilderGateway(server, domainNames, blacklistAndWhitelists, rateLimits)); - await Task.Delay(1000); + for (int i = 0; i < 10; i++) + { + // 等待服务启动 + if (Gateway.Gateway.CheckServerOnline(id)) + { + break; + } + + await Task.Delay(1000); + } } else { @@ -113,11 +124,9 @@ public static class ServerService throw new ValidationException("服务不存在"); } - var domainNames = await dbContext.DomainNames.Where(x => x.ServerId == id).ToListAsync(); + Gateway.Gateway.ReloadGateway(server, + await dbContext.DomainNames.Where(x => x.ServerId == id).ToArrayAsync()); - Gateway.Gateway.ReloadGateway(server, domainNames); - - await Task.Delay(1000); }).WithDescription("重载服务").WithDisplayName("重载服务").WithTags("服务"); return app;