Kaynağa Gözat

!1929 增加es多集群控制。
Merge pull request !1929 from 胖太乙/v2

zuohuaijun 5 ay önce
ebeveyn
işleme
bee8efd086

+ 28 - 0
Admin.NET/Admin.NET.Application/Configuration/ElasticSearch.json

@@ -0,0 +1,28 @@
+{
+  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
+
+  "ElasticSearch:Logging": {
+    "Enabled": true, // 启用ES日志
+    "AuthType": "None", // ES认证类型,可选 Basic、ApiKey、Base64ApiKey、None
+    "User": "elastic", // Basic认证的用户名,使用Basic认证类型时必填
+    "Password": "123456", // Basic认证的密码,使用Basic认证类型时必填
+    "ApiId": "", // 使用ApiKey认证类型时必填
+    "ApiKey": "", // 使用ApiKey认证类型时必填
+    "Base64ApiKey": "TmtrOEszNEJuQ0NyaWlydGtROFk6SG1RZ0w3YzBTc2lCanJTYlV3aXNzZw==", // 使用Base64ApiKey认证类型时必填
+    "Fingerprint": "37:08:6A:C6:06:CC:9A:43:CF:ED:25:A2:1C:A4:69:57:90:31:2C:06:CA:61:56:39:6A:9C:46:11:BD:22:51:DA", // ES使用Https时的证书指纹
+    "ServerUris": [ "http://192.168.3.90:9200" ], // 地址
+    "DefaultIndex": "adminnet" // 索引
+  },
+  "ElasticSearch:Business": {
+    "Enabled": false, // 启用ES日志
+    "AuthType": "Basic", // ES认证类型,可选 Basic、ApiKey、Base64ApiKey、None
+    "User": "admin", // Basic认证的用户名,使用Basic认证类型时必填
+    "Password": "123456", // Basic认证的密码,使用Basic认证类型时必填
+    "ApiId": "", // 使用ApiKey认证类型时必填
+    "ApiKey": "", // 使用ApiKey认证类型时必填
+    "Base64ApiKey": "TmtrOEszNEJuQ0NyaWlydGtROFk6SG1RZ0w3YzBTc2lCanJTYlV3aXNzZw==", // 使用Base64ApiKey认证类型时必填
+    "Fingerprint": "37:08:6A:C6:06:CC:9A:43:CF:ED:25:A2:1C:A4:69:57:90:31:2C:06:CA:61:56:39:6A:9C:46:11:BD:22:51:DA", // ES使用Https时的证书指纹
+    "ServerUris": [ "http://192.168.1.100:9200" ], // 地址
+    "DefaultIndex": "adminnet" // 索引
+  }
+}

+ 0 - 12
Admin.NET/Admin.NET.Application/Configuration/Logging.json

@@ -21,18 +21,6 @@
       "Enabled": true, // 启用数据库日志
       "MinimumLevel": "Information" // 日志级别
     },
-    "ElasticSearch": {
-      "Enabled": false, // 启用ES日志
-      "AuthType": "Basic", // ES认证类型,可选 Basic、ApiKey、Base64ApiKey
-      "User": "admin", // Basic认证的用户名,使用Basic认证类型时必填
-      "Password": "123456", // Basic认证的密码,使用Basic认证类型时必填
-      "ApiId": "", // 使用ApiKey认证类型时必填
-      "ApiKey": "", // 使用ApiKey认证类型时必填
-      "Base64ApiKey": "TmtrOEszNEJuQ0NyaWlydGtROFk6SG1RZ0w3YzBTc2lCanJTYlV3aXNzZw==", // 使用Base64ApiKey认证类型时必填
-      "Fingerprint": "37:08:6A:C6:06:CC:9A:43:CF:ED:25:A2:1C:A4:69:57:90:31:2C:06:CA:61:56:39:6A:9C:46:11:BD:22:51:DA", // ES使用Https时的证书指纹
-      "ServerUris": [ "http://192.168.1.100:9200" ], // 地址
-      "DefaultIndex": "adminnet" // 索引
-    },
     "Monitor": {
       "GlobalEnabled": true, // 启用全局拦截日志(建议生产环境关闭,否则对性能有影响)
       "IncludeOfMethods": [], // 拦截特定方法,当GlobalEnabled=false有效

+ 47 - 0
Admin.NET/Admin.NET.Core/ElasticSearch/ElasticSearchClientContainer.cs

@@ -0,0 +1,47 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Elastic.Clients.Elasticsearch;
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// ES客户端容器
+/// </summary>
+public class ElasticSearchClientContainer
+{
+    private readonly Dictionary<EsClientTypeEnum, ElasticsearchClient> _clients;
+
+    /// <summary>
+    /// 初始化容器(通过字典注入所有客户端)
+    /// </summary>
+    public ElasticSearchClientContainer(Dictionary<EsClientTypeEnum, ElasticsearchClient> clients)
+    {
+        _clients = clients ?? throw new ArgumentNullException(nameof(clients));
+    }
+
+    /// <summary>
+    /// 日志专用客户端
+    /// </summary>
+    public ElasticsearchClient Logging => GetClient(EsClientTypeEnum.Logging);
+
+    /// <summary>
+    /// 业务数据同步客户端
+    /// </summary>
+    public ElasticsearchClient Business => GetClient(EsClientTypeEnum.Business);
+
+    /// <summary>
+    /// 根据类型获取客户端(内部校验,避免未注册的类型)
+    /// </summary>
+    private ElasticsearchClient GetClient(EsClientTypeEnum type)
+    {
+        if (_clients.TryGetValue(type, out var client))
+        {
+            return client;
+        }
+        throw new KeyNotFoundException($"未注册的ES客户端类型:{type},请检查注册配置");
+    }
+}

+ 82 - 0
Admin.NET/Admin.NET.Core/ElasticSearch/ElasticSearchClientFactory.cs

@@ -0,0 +1,82 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Elastic.Clients.Elasticsearch;
+using Elastic.Transport;
+
+namespace Admin.NET.Core;
+
+public class ElasticSearchClientFactory
+{
+    /// <summary>
+    /// 创建 ES 客户端(通用方法)
+    /// </summary>
+    /// <typeparam name="TOptions">配置类型(支持通用或场景专用)</typeparam>
+    /// <param name="configPath">配置文件路径(如 "ElasticSearch:Logging")</param>
+    /// <returns>ES 客户端实例(或 null  if 未启用)</returns>
+    public static ElasticsearchClient? CreateClient<TOptions>(string configPath) where TOptions : ElasticSearchOptions, new()
+    {
+        // 从配置文件读取当前场景的配置
+        var options = App.GetConfig<TOptions>(configPath);
+        if (options == null)
+            throw Oops.Oh($"未找到{configPath}配置项");
+
+        if (!options.Enabled)
+            return null;
+
+        // 验证服务地址
+        if (options.ServerUris == null || !options.ServerUris.Any())
+            throw new ArgumentException($"ES 配置 {configPath} 未设置 ServerUris");
+
+        // 构建连接池(支持集群)
+        var uris = options.ServerUris.Select(uri => new Uri(uri)).ToList();
+        var connectionPool = new StaticNodePool(uris);
+        var connectionSettings = new ElasticsearchClientSettings(connectionPool)
+            .DefaultIndex(options.DefaultIndex) // 设置默认索引
+            .DisableDirectStreaming()  // 开启请求/响应日志,方便排查问题
+            .OnRequestCompleted(response =>
+            {
+                if (response.HttpStatusCode == 401)
+                {
+                    Console.WriteLine("ES 请求被拒绝:未提供有效认证信息");
+                }
+            });
+
+        // 配置认证
+        ConfigureAuthentication(connectionSettings, options);
+
+        // 配置 HTTPS 证书指纹
+        if (!string.IsNullOrEmpty(options.Fingerprint))
+            connectionSettings.CertificateFingerprint(options.Fingerprint);
+
+        return new ElasticsearchClient(connectionSettings);
+    }
+
+    /// <summary>
+    /// 配置认证(通用逻辑)
+    /// </summary>
+    private static void ConfigureAuthentication(ElasticsearchClientSettings settings, ElasticSearchOptions options)
+    {
+        switch (options.AuthType)
+        {
+            case ElasticSearchAuthTypeEnum.Basic:
+                settings.Authentication(new BasicAuthentication(options.User, options.Password));
+                break;
+            case ElasticSearchAuthTypeEnum.ApiKey:
+                settings.Authentication(new ApiKey(options.ApiKey));
+                break;
+            case ElasticSearchAuthTypeEnum.Base64ApiKey:
+                settings.Authentication(new Base64ApiKey(options.Base64ApiKey));
+                break;
+            case ElasticSearchAuthTypeEnum.None:
+                // 无需认证
+                break;
+            default:
+                throw new ArgumentOutOfRangeException(nameof(options.AuthType));
+        }
+    }
+}
+

+ 41 - 0
Admin.NET/Admin.NET.Core/ElasticSearch/ElasticSearchSetup.cs

@@ -0,0 +1,41 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Elastic.Clients.Elasticsearch;
+
+namespace Admin.NET.Core.ElasticSearch;
+
+/// <summary>
+/// ES服务注册
+/// </summary>
+public static class ElasticSearchSetup
+{
+    /// <summary>
+    /// 注册所有ES客户端(日志+业务)
+    /// </summary>
+    public static void AddElasticSearchClients(this IServiceCollection services)
+    {
+        // 1. 创建客户端字典(枚举→客户端实例)
+        var clients = new Dictionary<EsClientTypeEnum, ElasticsearchClient>();
+
+        // 2. 注册日志客户端
+        var loggingClient = ElasticSearchClientFactory.CreateClient<ElasticSearchOptions>(configPath: "ElasticSearch:Logging");
+        if (loggingClient != null)
+        {
+            clients[EsClientTypeEnum.Logging] = loggingClient;
+        }
+
+        // 3. 注册业务客户端
+        var businessClient = ElasticSearchClientFactory.CreateClient<ElasticSearchOptions>(configPath: "ElasticSearch:Business");
+        if (businessClient != null)
+        {
+            clients[EsClientTypeEnum.Business] = businessClient;
+        }
+
+        // 4. 将客户端容器注册为单例(全局唯一)
+        services.AddSingleton(new ElasticSearchClientContainer(clients));
+    }
+}

+ 7 - 1
Admin.NET/Admin.NET.Core/Enum/ElasticSearchAuthTypeEnum.cs

@@ -29,5 +29,11 @@ public enum ElasticSearchAuthTypeEnum
     /// Base64ApiKey
     /// </summary>
     [Description("Base64ApiKey")]
-    Base64ApiKey = 3
+    Base64ApiKey = 3,
+
+    /// <summary>
+    /// 不验证
+    /// </summary>
+    [Description("None")]
+    None = 4
 }

+ 23 - 0
Admin.NET/Admin.NET.Core/Enum/EsClientTypeEnum.cs

@@ -0,0 +1,23 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// ES客户端类型(标识不同场景)
+/// </summary>
+public enum EsClientTypeEnum
+{
+    /// <summary>
+    /// 日志专用
+    /// </summary>
+    Logging,
+
+    /// <summary>
+    /// 业务数据
+    /// </summary>
+    Business
+}

+ 1 - 1
Admin.NET/Admin.NET.Core/Logging/ElasticSearchLoggingWriter.cs

@@ -20,7 +20,7 @@ public class ElasticSearchLoggingWriter : IDatabaseLoggingWriter, IDisposable
     public ElasticSearchLoggingWriter(IServiceScopeFactory scopeFactory)
     {
         _serviceScope = scopeFactory.CreateScope();
-        _esClient = _serviceScope.ServiceProvider.GetRequiredService<ElasticsearchClient>();
+        _esClient = _serviceScope.ServiceProvider.GetRequiredService<ElasticSearchClientContainer>()?.Logging;
         _sysConfigService = _serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
     }
 

+ 0 - 53
Admin.NET/Admin.NET.Core/Logging/ElasticSearchSetup.cs

@@ -1,53 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Elastic.Clients.Elasticsearch;
-using Elastic.Transport;
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// ES服务注册
-/// </summary>
-public static class ElasticSearchSetup
-{
-    public static void AddElasticSearch(this IServiceCollection services)
-    {
-        var option = App.GetConfig<ElasticSearchOptions>("Logging:ElasticSearch");
-        if (!option.Enabled) return;
-
-        var uris = option.ServerUris.Select(u => new Uri(u));
-        // 集群
-        var connectionPool = new StaticNodePool(uris);
-        var connectionSettings = new ElasticsearchClientSettings(connectionPool).DefaultIndex(option.DefaultIndex);
-        // 单连接
-        //var connectionSettings = new ElasticsearchClientSettings(new StaticNodePool(new List<Uri> { uris.FirstOrDefault() })).DefaultIndex(option.DefaultIndex);
-
-        // 认证类型
-        if (option.AuthType == ElasticSearchAuthTypeEnum.Basic) // Basic 认证
-        {
-            connectionSettings.Authentication(new BasicAuthentication(option.User, option.Password));
-        }
-        else if (option.AuthType == ElasticSearchAuthTypeEnum.ApiKey) // ApiKey 认证
-        {
-            connectionSettings.Authentication(new ApiKey(option.ApiKey));
-        }
-        else if (option.AuthType == ElasticSearchAuthTypeEnum.Base64ApiKey) // Base64ApiKey 认证
-        {
-            connectionSettings.Authentication(new Base64ApiKey(option.Base64ApiKey));
-        }
-        else return;
-
-        // ES使用Https时的证书指纹
-        if (!string.IsNullOrEmpty(option.Fingerprint))
-        {
-            connectionSettings.CertificateFingerprint(option.Fingerprint);
-        }
-
-        var client = new ElasticsearchClient(connectionSettings);
-        services.AddSingleton(client); // 单例注册
-    }
-}

+ 1 - 1
Admin.NET/Admin.NET.Core/Logging/LoggingSetup.cs

@@ -65,7 +65,7 @@ public static class LoggingSetup
         }
 
         // 日志写入ElasticSearch
-        if (App.GetConfig<bool>("Logging:ElasticSearch:Enabled", true))
+        if (App.GetConfig<bool>("ElasticSearch:Logging:Enabled", true))
         {
             services.AddDatabaseLogging<ElasticSearchLoggingWriter>(options =>
             {

+ 2 - 1
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -5,6 +5,7 @@
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Admin.NET.Core;
+using Admin.NET.Core.ElasticSearch;
 using Admin.NET.Core.Service;
 using AspNetCoreRateLimit;
 using Furion;
@@ -117,7 +118,7 @@ public class Startup : AppStartup
         services.AddOAuth();
 
         // ElasticSearch
-        services.AddElasticSearch();
+        services.AddElasticSearchClients();
 
         // 配置Nginx转发获取客户端真实IP
         // 注1:如果负载均衡不是在本机通过 Loopback 地址转发请求的,一定要加上options.KnownNetworks.Clear()和options.KnownProxies.Clear()