diff --git a/src/Gateway/BackgroundServices/GatewayBackground.cs b/src/Gateway/BackgroundServices/GatewayBackground.cs
index adc450cda70da5bf80813d26b36822ee4f8de914..4db3a71c35edc6b22ad205993b1a1c3c108d82d2 100644
--- a/src/Gateway/BackgroundServices/GatewayBackground.cs
+++ b/src/Gateway/BackgroundServices/GatewayBackground.cs
@@ -9,10 +9,50 @@ public class GatewayBackgroundService(
{
// 数据库迁移
freeSql.CodeFirst.SyncStructure(typeof(RouteEntity), typeof(ClusterEntity), typeof(CertificateEntity),
- typeof(StaticFileProxyEntity));
+ typeof(StaticFileProxyEntity), typeof(SystemLoggerEntity));
// 首次启动时更新配置
await gatewayService.RefreshConfig();
await certificateService.RefreshConfig();
+
+ while (stoppingToken.IsCancellationRequested == false)
+ {
+ // 获取当前时间距离23:59:59剩余多少秒,然后等待这么多秒
+ var seconds = (DateTime.Now.Date.AddDays(1) - DateTime.Now).TotalSeconds;
+
+ // Task.Delay不能是负数
+ if (seconds < 0)
+ {
+ seconds = 1000;
+ }
+
+ #region 等待第二天凌晨
+
+ // 如果服务手动被关闭,则先统计当天流量记录。
+ try
+ {
+ await Task.Delay(TimeSpan.FromSeconds(seconds), stoppingToken);
+
+ // 停止一秒防止时间计算错误
+ await Task.Delay(1000, stoppingToken);
+ }
+ catch
+ {
+ }
+
+ #endregion
+
+ var systemLoggerEntity = new SystemLoggerEntity
+ {
+ RequestCount = GatewayMiddleware.CurrentRequestCount,
+ ErrorRequestCount = GatewayMiddleware.CurrentErrorCount,
+ CurrentTime = DateTime.Now.AddDays(-1)
+ };
+
+ // 清空请求计数器
+ GatewayMiddleware.ClearRequestCount();
+
+ await freeSql.Insert(systemLoggerEntity).ExecuteAffrowsAsync();
+ }
}
}
\ No newline at end of file
diff --git a/src/Gateway/Dto/NetWorkDto.cs b/src/Gateway/Dto/NetWorkDto.cs
index 9d3b531f8f01b13ae10406afe8a6f92c77224140..a0fdc69acc691df3765a59abc7a28fe2b9a27bbd 100644
--- a/src/Gateway/Dto/NetWorkDto.cs
+++ b/src/Gateway/Dto/NetWorkDto.cs
@@ -28,4 +28,36 @@ public class NetWorkDto(long received, long sent)
/// 当前时间
///
public string Time => DateTime.Now.ToString("HH:mm:ss");
+
+ ///
+ /// 当天请求数量
+ ///
+ public int CurrentRequestCount { get; set; } = GatewayMiddleware.CurrentRequestCount;
+
+ ///
+ /// 当天错误数量
+ ///
+ public int CurrentErrorCount { get; set; } = GatewayMiddleware.CurrentErrorCount;
+
+ private double _totalRequestCount;
+
+ ///
+ /// 当天错误率
+ ///
+ public double TotalRequestCount
+ {
+ get => _totalRequestCount + CurrentRequestCount;
+ set => _totalRequestCount = value;
+ }
+
+ private double _totalErrorCount;
+
+ ///
+ /// 总错误数量
+ ///
+ public double TotalErrorCount
+ {
+ get => _totalErrorCount + CurrentErrorCount;
+ set => value = _totalErrorCount;
+ }
}
\ No newline at end of file
diff --git a/src/Gateway/Entities/SystemLoggerEntity.cs b/src/Gateway/Entities/SystemLoggerEntity.cs
new file mode 100644
index 0000000000000000000000000000000000000000..89ad2ec04f663bfe25d227007538ee620ec8f950
--- /dev/null
+++ b/src/Gateway/Entities/SystemLoggerEntity.cs
@@ -0,0 +1,21 @@
+namespace Gateway.Entities;
+
+public sealed class SystemLoggerEntity : Entity
+{
+ ///
+ /// 请求数量
+ ///
+ public int RequestCount { get; set; }
+
+ ///
+ /// 异常数量
+ ///
+ public int ErrorRequestCount { get; set; }
+
+ ///
+ /// 当前时间
+ ///
+ public DateTime CurrentTime { get; set; }
+
+
+}
\ No newline at end of file
diff --git a/src/Gateway/Middlewares/GatewayMiddleware.cs b/src/Gateway/Middlewares/GatewayMiddleware.cs
index 966ff75890cc4840e16fb78cdc1d5af152842d71..46a39fe2495a1cfbdea3bec6b5b88c553668cc95 100644
--- a/src/Gateway/Middlewares/GatewayMiddleware.cs
+++ b/src/Gateway/Middlewares/GatewayMiddleware.cs
@@ -2,6 +2,26 @@
public class GatewayMiddleware : IMiddleware
{
+ ///
+ /// 请求计数器
+ ///
+ private static int _currentRequestCount;
+
+ ///
+ /// 异常计数器
+ ///
+ private static int _currentErrorCount;
+
+ public static int CurrentRequestCount => _currentRequestCount;
+
+ public static int CurrentErrorCount => _currentErrorCount;
+
+ public static void ClearRequestCount()
+ {
+ Interlocked.Exchange(ref _currentRequestCount, 0);
+ Interlocked.Exchange(ref _currentErrorCount, 0);
+ }
+
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// TODO: 由于h3需要对应请求的端口,所以这里需要动态设置
@@ -14,6 +34,18 @@ public class GatewayMiddleware : IMiddleware
context.Response.Headers["Gateway-Version"] = GatewayOptions.Version;
+ // Gateway默认的请求不记录
+ var record = context.Request.Path.Value?.StartsWith("/api/gateway/") == false;
+
+ // 使用原子操作,防止并发问题
+ if (record)
+ Interlocked.Increment(ref _currentRequestCount);
+
await next(context);
+
+ if (record && context.Response.StatusCode >= 400)
+ {
+ Interlocked.Increment(ref _currentErrorCount);
+ }
}
}
\ No newline at end of file
diff --git a/src/Gateway/Program.cs b/src/Gateway/Program.cs
index c50e208b83790ae08aef69292170d1e60930e1c8..199f3c7382d6d2edadfa6bb4cd1de662e49c7222 100644
--- a/src/Gateway/Program.cs
+++ b/src/Gateway/Program.cs
@@ -28,8 +28,8 @@ builder.Configuration.GetSection(GatewayOptions.Name)
.Get();
// 获取环境变量
-var https_password = Environment.GetEnvironmentVariable("HTTPS_PASSWORD") ?? "dd666666";
-var https_file = Environment.GetEnvironmentVariable("HTTPS_FILE") ?? "gateway.pfx";
+var httpsPassword = Environment.GetEnvironmentVariable("HTTPS_PASSWORD") ?? "dd666666";
+var httpsFile = Environment.GetEnvironmentVariable("HTTPS_FILE") ?? "gateway.pfx";
builder.WebHost.UseKestrel(options =>
{
@@ -43,8 +43,8 @@ builder.WebHost.UseKestrel(options =>
!CertificateService.CertificateEntityDict.TryGetValue(name, out var certificate))
{
// 创建一个默认的证书
- return new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "certificates", https_file),
- https_password);
+ return new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "certificates", httpsFile),
+ httpsPassword);
}
var path = Path.Combine("/data/", certificate.Path);
@@ -55,8 +55,8 @@ builder.WebHost.UseKestrel(options =>
Console.WriteLine($"证书文件不存在:{path}");
Console.ResetColor();
- return new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "certificates", https_file),
- https_password);
+ return new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "certificates", httpsFile),
+ httpsPassword);
};
});
});
@@ -69,6 +69,7 @@ builder.WebHost.ConfigureKestrel(kestrel =>
{
portOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
portOptions.UseHttps();
+
});
kestrel.ListenAnyIP(8080, portOptions => { portOptions.Protocols = HttpProtocols.Http1AndHttp2; });
diff --git a/src/Gateway/Services/SystemService.cs b/src/Gateway/Services/SystemService.cs
index 830edf9eac7966cfa5316270cefb9c0cf191182c..ad2cea30c7f56ff6804b715975191e8eaaa133df 100644
--- a/src/Gateway/Services/SystemService.cs
+++ b/src/Gateway/Services/SystemService.cs
@@ -1,14 +1,20 @@
namespace Gateway.Services;
-public class SystemService
+///
+/// 系统服务
+///
+public static class SystemService
{
- public static async Task StreamAsync(HttpContext context)
+ public static async Task StreamAsync(HttpContext context, IFreeSql sql)
{
// 使用sse,返回响应头
context.Response.Headers.ContentType = "text/event-stream";
var i = 0;
+ var totalErrorCount = (double)await sql.Select().SumAsync(x => x.ErrorRequestCount);
+ var totalRequestCount = (double)await sql.Select().SumAsync(x => x.RequestCount);
+
while (!context.RequestAborted.IsCancellationRequested)
{
// 获取所有网络接口
@@ -27,7 +33,7 @@ public class SystemService
initialBytesSent += interfaceStats.BytesSent;
initialBytesReceived += interfaceStats.BytesReceived;
}
-
+
// 等待1秒钟
await Task.Delay(1000, context.RequestAborted);
@@ -50,7 +56,11 @@ public class SystemService
var totalBytesReceivedIn1Sec = bytesReceivedAfter1Sec - initialBytesReceived;
var data =
- $"data:{JsonSerializer.Serialize(new NetWorkDto(totalBytesReceivedIn1Sec, totalBytesSentIn1Sec))}\n\n";
+ $"data:{JsonSerializer.Serialize(new NetWorkDto(totalBytesReceivedIn1Sec, totalBytesSentIn1Sec)
+ {
+ TotalErrorCount = totalErrorCount,
+ TotalRequestCount = totalRequestCount
+ })}\n\n";
// 将数据写入到响应流中
await context.Response.WriteAsync(data, context.RequestAborted);
@@ -58,8 +68,8 @@ public class SystemService
i++;
- // 只维持5秒的连接
- if (i > 5)
+ // 只维持10秒的连接
+ if (i > 10)
{
break;
}
@@ -71,7 +81,7 @@ public static class SystemExtension
{
public static void MapSystem(this IEndpointRouteBuilder app)
{
- app.MapGet("/api/gateway/system", async context =>
- await SystemService.StreamAsync(context));
+ app.MapGet("/api/gateway/system", async (HttpContext context, IFreeSql sql) =>
+ await SystemService.StreamAsync(context, sql));
}
}
\ No newline at end of file
diff --git a/web/src/pages/home/index.tsx b/web/src/pages/home/index.tsx
index 8f4a4a8cf61cac41c3b53f7074015e3a56082654..88de3ba913a6864c5a1c39ed094aeecf0846ab03 100644
--- a/web/src/pages/home/index.tsx
+++ b/web/src/pages/home/index.tsx
@@ -1,9 +1,14 @@
import { useEffect, useState } from "react";
-import { Col, Row } from '@douyinfe/semi-ui';
+import { Card, Col, Row } from '@douyinfe/semi-ui';
import { stream } from '../../service/NetWorkService';
import * as echarts from 'echarts';
export default function Home() {
+ const [totalRequestCount, setTotalRequestCount] = useState(0);
+ const [totalErrorCount, setTotalErrorCount] = useState(0);
+ const [currentRequestCount, setCurrentRequestCount] = useState(0);
+ const [currentErrorCount, setCurrentErrorCount] = useState(0);
+
useEffect(() => {
var chartDom = document.getElementById('network')!;
var myChart = echarts.init(chartDom);
@@ -124,6 +129,12 @@ export default function Home() {
option.xAxis.data.push(chunk.Time);
option.series[0].data.push(chunk.Sent);
option.series[1].data.push(chunk.Received);
+
+ setCurrentErrorCount(chunk.CurrentErrorCount);
+ setCurrentRequestCount(chunk.CurrentRequestCount);
+ setTotalErrorCount(chunk.TotalErrorCount);
+ setTotalRequestCount(chunk.TotalRequestCount);
+
option && myChart.setOption(option);
myChart.resize();
@@ -137,7 +148,7 @@ export default function Home() {
fetchData();
// 设置定时器,每隔一段时间调用获取数据的函数
- const intervalId = setInterval(fetchData, 5900); // 每5秒获取一次数据
+ const intervalId = setInterval(fetchData, 10900); // 每5秒获取一次数据
// 组件卸载时清除定时器
return () => {
@@ -158,6 +169,28 @@ export default function Home() {
}}>
数据面板
+
+
+
+ 总请求数:{totalRequestCount}
+
+
+
+
+ 总错误数:{totalErrorCount}
+
+
+
+
+ 当天请求数:{currentRequestCount}
+
+
+
+
+ 当天错误数:{currentErrorCount}
+
+
+