diff --git a/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs b/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs index 16b9401..eaa081b 100644 --- a/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs +++ b/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs @@ -24,7 +24,6 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts { _logger = logger; _protocolInfoRepository = serviceProvider.GetRequiredService>(); - } diff --git a/web/JiShe.CollectBus.Host/CollectBusHostConst.cs b/web/JiShe.CollectBus.Host/CollectBusHostConst.cs index 319dfed..4ec4f70 100644 --- a/web/JiShe.CollectBus.Host/CollectBusHostConst.cs +++ b/web/JiShe.CollectBus.Host/CollectBusHostConst.cs @@ -1,5 +1,8 @@ namespace JiShe.CollectBus.Host { + /// + /// CollectBusHostConst + /// public static class CollectBusHostConst { /// @@ -23,9 +26,15 @@ public const string HangfireDashboardEndPoint = "/hangfire"; /// - /// CAP 端点 + /// 健康检查 端点 /// - public const string CapDashboardEndPoint = "/cap"; + public const string HealthEndPoint = "/health"; + + /// + /// 健康检查 端点 + /// + public const string HealthDashboardEndPoint = "/health-ui"; + } } diff --git a/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 517d5fe..e6f1e7e 100644 --- a/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -2,6 +2,7 @@ using Hangfire; using Hangfire.Redis.StackExchange; using JiShe.CollectBus.Host.Hangfire; +using JiShe.CollectBus.Host.HealthChecks; using JiShe.CollectBus.Host.Swaggers; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.DataProtection; @@ -16,6 +17,9 @@ using Volo.Abp.Modularity; using TouchSocket.Core; using TouchSocket.Sockets; using JiShe.CollectBus.Plugins; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using JiShe.CollectBus.Cassandra; namespace JiShe.CollectBus.Host @@ -206,7 +210,6 @@ namespace JiShe.CollectBus.Host private void ConfigureCustom(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddSingleton(); - context.Services.AddHealthChecks(); } /// @@ -241,5 +244,37 @@ namespace JiShe.CollectBus.Host .SetUdpDataHandlingAdapter(() => new NormalUdpDataHandlingAdapter()); }); } + + /// + /// 健康检查 + /// + /// + /// + private void ConfigureHealthChecks(ServiceConfigurationContext context, IConfiguration configuration) + { + if (!configuration.GetValue("HealthChecks:IsEnable")) return; + var cassandraConfig = new CassandraConfig(); + configuration.GetSection("Cassandra").Bind(cassandraConfig); + context.Services.AddHealthChecks() + .AddMongoDb(configuration.GetConnectionString("Default"), "MongoDB", HealthStatus.Unhealthy) + .AddRedis(configuration.GetValue("Redis:Configuration") ?? string.Empty, "Redis", + HealthStatus.Unhealthy) + .AddKafka(new Confluent.Kafka.ProducerConfig + { + BootstrapServers = configuration.GetConnectionString("Kafka") + }, "Kafka", failureStatus: HealthStatus.Unhealthy) + + .AddCheck("Cassandra") + .AddCheck("IoTDB"); + + context.Services + .AddHealthChecksUI(options => + { + options.AddHealthCheckEndpoint("JiSheCollectBus", "/health"); // 映射本地端点 + }) + .AddInMemoryStorage(); + + + } } } \ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/CollectBusHostModule.cs b/web/JiShe.CollectBus.Host/CollectBusHostModule.cs index b038907..5068663 100644 --- a/web/JiShe.CollectBus.Host/CollectBusHostModule.cs +++ b/web/JiShe.CollectBus.Host/CollectBusHostModule.cs @@ -1,4 +1,5 @@ using Hangfire; +using HealthChecks.UI.Client; using JiShe.CollectBus.Host.Extensions; using JiShe.CollectBus.Host.HealthChecks; using JiShe.CollectBus.Host.Swaggers; @@ -45,9 +46,9 @@ namespace JiShe.CollectBus.Host ConfigureNetwork(context, configuration); ConfigureJwtAuthentication(context, configuration); ConfigureHangfire(context); - //ConfigureKafkaTopic(context, configuration); ConfigureAuditLog(context); ConfigureCustom(context, configuration); + ConfigureHealthChecks(context, configuration); Configure(options => { options.Kind = DateTimeKind.Local; }); } @@ -89,11 +90,16 @@ namespace JiShe.CollectBus.Host }); app.UseConfiguredEndpoints(endpoints => { + if (!configuration.GetValue("HealthChecks:IsEnable")) return; endpoints.MapHealthChecks("/health", new HealthCheckOptions { Predicate = _ => true, ResponseWriter = HealthCheckResponse.Writer }); + endpoints.MapHealthChecksUI(options => + { + options.UIPath = "/health-ui"; + }); }); } } diff --git a/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs b/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs new file mode 100644 index 0000000..1157239 --- /dev/null +++ b/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs @@ -0,0 +1,57 @@ +using Cassandra; +using JiShe.CollectBus.Cassandra; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace JiShe.CollectBus.Host.HealthChecks +{ + /// + /// CassandraHealthCheck + /// + /// + public class CassandraHealthCheck : IHealthCheck + { + private readonly IConfiguration _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + public CassandraHealthCheck(IConfiguration configuration) + { + _configuration = configuration; + } + + /// + /// Runs the health check, returning the status of the component being checked. + /// + /// A context object associated with the current execution. + /// A that can be used to cancel the health check. + /// + /// A that completes when the health check has finished, yielding the status of the component being checked. + /// + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + var cassandraConfig = new CassandraConfig(); + _configuration.GetSection("Cassandra").Bind(cassandraConfig); + try + { + var clusterBuilder = Cluster.Builder(); + foreach (var node in cassandraConfig.Nodes) + { + clusterBuilder.AddContactPoint(node.Host) + .WithPort(node.Port); + } + clusterBuilder.WithCredentials(cassandraConfig.Username, cassandraConfig.Password); + var cluster = clusterBuilder.Build(); + using var session = await cluster.ConnectAsync(); + var result = await Task.FromResult(session.Execute("SELECT release_version FROM system.local")); + var version = result.First().GetValue("release_version"); + return HealthCheckResult.Healthy($"Cassandra is healthy. Version: {version}"); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, $"Cassandra is unhealthy: {ex.Message}", ex); + } + } + } +} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs b/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs new file mode 100644 index 0000000..d3cb45f --- /dev/null +++ b/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs @@ -0,0 +1,51 @@ +using System.Net.Sockets; +using JiShe.CollectBus.Cassandra; +using JiShe.CollectBus.IoTDB.Interface; +using JiShe.CollectBus.IoTDB.Options; +using JiShe.CollectBus.IoTDB.Provider; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace JiShe.CollectBus.Host.HealthChecks +{ + /// + /// IoTDBHealthCheck + /// + /// + public class IoTdbHealthCheck : IHealthCheck + { + private readonly IConfiguration _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + public IoTdbHealthCheck(IConfiguration configuration) + { + _configuration = configuration; + } + + /// + /// Runs the health check, returning the status of the component being checked. + /// + /// A context object associated with the current execution. + /// A that can be used to cancel the health check. + /// + /// A that completes when the health check has finished, yielding the status of the component being checked. + /// + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var ioTDbOptions = new IoTDbOptions(); + _configuration.GetSection("IoTDBOptions").Bind(ioTDbOptions); + var pool = new SessionPoolAdapter(ioTDbOptions); + await pool.OpenAsync(); + return HealthCheckResult.Healthy($"IoTDB is healthy."); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, $"IoTDB不健康: {ex.Message}", ex); + } + } + } +} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj b/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj index 86f49a6..ffb3536 100644 --- a/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj +++ b/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj @@ -20,9 +20,14 @@ + + + + + + + - - diff --git a/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml b/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml index 9e1d029..3a7fbc7 100644 --- a/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml +++ b/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml @@ -11,59 +11,59 @@ - - - - + + + + 后端服务 - - - -
- -
-
-
- - - -
-

- SwaggerUI -

-
-
-
-
-
- - - -
-

- Hangfire面板 -

+ +
+ +
+
+
+ + + +
+

+ SwaggerUI +

+
-
-
-
- - - -
-

- CAP -

+ +
+
+ + + +
+

+ Hangfire面板 +

+
-
- @* *@ +
-
\ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml.cs b/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml.cs index 6019db9..1e18bdb 100644 --- a/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml.cs +++ b/web/JiShe.CollectBus.Host/Pages/Monitor.cshtml.cs @@ -4,6 +4,7 @@ namespace JiShe.CollectBus.Host.Pages { public class Monitor : PageModel { + public void OnGet() { diff --git a/web/JiShe.CollectBus.Host/Program.cs b/web/JiShe.CollectBus.Host/Program.cs index d75a227..3e6bdfd 100644 --- a/web/JiShe.CollectBus.Host/Program.cs +++ b/web/JiShe.CollectBus.Host/Program.cs @@ -1,12 +1,16 @@ -using JiShe.CollectBus.Host; -using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Serilog; using Volo.Abp.Modularity.PlugIns; +namespace JiShe.CollectBus.Host; + +/// +/// Program +/// public class Program { /// - /// + /// Main /// /// /// @@ -19,47 +23,13 @@ public class Program loggerConfiguration.ReadFrom.Configuration(context.Configuration); }) .UseAutofac(); + var configuration = builder.Configuration; await builder.AddApplicationAsync(options => { - // ز̶ģʽȲ - options.PlugInSources.AddFolder(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins")); + options.PlugInSources.AddFolder((configuration["PlugInFolder"].IsNullOrWhiteSpace()? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"): configuration["PlugInFolder"]) ?? string.Empty); }); var app = builder.Build(); await app.InitializeApplicationAsync(); await app.RunAsync(); - - - //await CreateHostBuilder(args).Build().RunAsync(); - } - - private static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseSerilog((context, loggerConfiguration) => - { - loggerConfiguration.ReadFrom.Configuration(context.Configuration); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }) - .UseAutofac(); - - - - - private static IHostBuilder CreateConsoleHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureServices((hostContext, services) => { ConfigureServices(services, hostContext); }) - .UseAutofac() - .UseSerilog((context, loggerConfiguration) => - { - loggerConfiguration.ReadFrom.Configuration(context.Configuration); - }); - - - private static async Task ConfigureServices(IServiceCollection services, HostBuilderContext hostContext) - { - await services.AddApplicationAsync(); } } \ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/Startup.cs b/web/JiShe.CollectBus.Host/Startup.cs deleted file mode 100644 index 32740ee..0000000 --- a/web/JiShe.CollectBus.Host/Startup.cs +++ /dev/null @@ -1,53 +0,0 @@ -using TouchSocket.Core; -using Volo.Abp.Modularity.PlugIns; - -namespace JiShe.CollectBus.Host -{ - /// - /// Startup - /// - public class Startup - { - private readonly IConfiguration _configuration; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - public Startup(IConfiguration configuration) - { - _configuration = configuration; - } - - /// - /// Configures the services. - /// - /// The services. - public void ConfigureServices(IServiceCollection services) - { - services.AddApplication(options => - { - // 加载插件,固定模式,可热插拔 - options.PlugInSources.AddFolder(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins")); - }); - } - - /// - /// Configures the specified application. - /// - /// The application. - /// The lifetime. - public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime) - { - - app.Use(async (context, next) => - { - // 在请求处理之前调用 InitializeApplicationAsync - await app.InitializeApplicationAsync(); - - // 继续请求管道中的下一个中间件 - await next(); - }); - } - } -} diff --git a/web/JiShe.CollectBus.Host/appsettings.json b/web/JiShe.CollectBus.Host/appsettings.json index 9b3838b..c98e05d 100644 --- a/web/JiShe.CollectBus.Host/appsettings.json +++ b/web/JiShe.CollectBus.Host/appsettings.json @@ -51,16 +51,11 @@ "Issuer": "JiShe.CollectBus", "ExpirationTime": 2 }, - "HealthCheck": { + "HealthChecks": { "IsEnable": true, - "MySql": { - "IsEnable": true - }, - "Pings": { - "IsEnable": true, - "Host": "https://www.baidu.com/", - "TimeOut": 5000 - } + "HealthCheckDatabaseName": "HealthChecks", + "EvaluationTimeInSeconds": 10, + "MinimumSecondsBetweenFailureNotifications": 60 }, "SwaggerConfig": [ { @@ -84,7 +79,7 @@ "SaslPassword": "lixiao1980", "KafkaReplicationFactor": 3, "NumPartitions": 30, - "ServerTagName": "JiSheCollectBus99", + "ServerTagName": "JiSheCollectBus30", "FirstCollectionTime": "2025-04-22 16:07:00" }, "IoTDBOptions": { @@ -95,7 +90,7 @@ "DataBaseName": "energy", "OpenDebugMode": true, "UseTableSessionPoolByDefault": false - }, + }, "Cassandra": { "ReplicationStrategy": { "Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下 @@ -144,5 +139,6 @@ "SerialConsistencyLevel": "Serial", "DefaultIdempotence": true } - } + }, + "PlugInFolder": "" } \ No newline at end of file