大家好,我是Edison。
上一篇:Logstash數據採集
寫在開頭
有了前面10篇的基礎,我們大概清楚了ES的基本概念和使用(主要是查詢),這也是我們作爲Developer應該瞭解的範疇,而至於更深入的聚合分析、運維管理就不在本系列的範疇中。有童鞋說,講講如何在.NET應用中集成和使用ES吧,額,這個其實網上也有很多教程了。不過你既然提了,那還是得安排上。
在.NET應用中集成ES一般涉及兩個方面:
(1)將ES當存儲用,類似於MongoDB,做文檔的增刪查改,這一類操作偏CRUD。
(2)對ES中的數據做查詢分析,聚合統計、分組等等,這一類操作偏查詢分析。
針對這兩類操作,我們今天先來搞定CRUD!
準備工作
創建一個Web API應用程序,這裏我選擇的是.NET 6版本。
項目結構如下圖所示:
其中,我們對Elastic的集成主要放在了Infrastructure目錄中,裏面存放了我們定義的Repository進而幫助我們在Controller中使用其提供的持久化能力。
在appsetting中我們定義瞭如下配置指明Elastic的相關信息(URL地址 和 默認索引):
{ ...... "Elastic": { "ServerUrl": "http://edt-elastic:9200", "DefaultIndex": "app-logs" } }
定義模型
我們這裏定義一個Model,假設叫AppLog,來模擬日誌文檔的CRUD到ES:
public class AppLog : ElasticModelBase { public string LogLevel { get; set; } = "Info"; public string Message { get; set; } = string.Empty; public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.Now; public AppLog() { this.Id = Guid.NewGuid().ToString(); } public AppLog(string id) { this.Id = id; } } public class ElasticModelBase { public virtual string Id { get; set; } }
定義ElasticProxy
我們知道,.NET應用要操作Elastic得使用Elastic的.NET客戶端,因此按照國際慣例我們對其進行一個小封裝,以便於可以在Repository中使用。
對於ES 7.x版本的集羣,推薦使用NEST這個客戶端包(目前已停止更新):
dotnet add package NEST
對於ES 8.x及以上版本的集羣,則推薦使用Elastic.Clients.Elasticsearch:
dotnet add package Elastic.Clients.Elasticsearch
這裏因爲我自己搭的實驗ES集羣環境是7.x版本,所以我使用的實NEST,不過操作都是類似的。
在Infrastructure目錄下,定義一個ElasticProxy用於獲取Elastic的.NET客戶端:
public interface IElasticProxy { IElasticClient GetClient(string indexName = null); } public class ElasticProxy : IElasticProxy { private readonly string ElasticServerUrl; private readonly string DefaultIndexName; public ElasticProxy(IConfiguration configuration) { ElasticServerUrl = configuration["Elastic:ServerUrl"] ?? throw new ArgumentNullException(); DefaultIndexName = configuration["Elastic:DefaultIndex"] ?? throw new ArgumentNullException(); } public IElasticClient GetClient(string indexName = null) { var settings = new ConnectionSettings(new Uri(ElasticServerUrl)) .DefaultIndex(indexName ?? DefaultIndexName); return new ElasticClient(settings); } }
定義ElasticRepository基類
爲了方便後續定義具體的Repository,我們這裏先定義一個ElasticRepository的基類,幫我們把CRUD操作做一個基礎的實現。
public interface IElasticRepositoryBase<T> where T : ElasticModelBase { Task AddAsync(T item); Task UpdateAsync(T item); Task DeleteAsync(string id); Task<Tuple<int, IList<T>>> QueryAsync(int page, int limit); } public abstract class ElasticRepositoryBase<T> : IElasticRepositoryBase<T> where T : ElasticModelBase { private readonly IElasticProxy _elasticProxy; public ElasticRepositoryBase(IElasticProxy elasticProxy) { _elasticProxy = elasticProxy; } protected abstract string IndexName { get; } protected IElasticClient Client => _elasticProxy.GetClient(IndexName); public virtual async Task AddAsync(T item) { await this.Client.IndexAsync(item, x => x.Index(this.IndexName)); } public virtual async Task DeleteAsync(string id) { await this.Client.DeleteAsync<T>(id, x => x.Index(this.IndexName)); } public virtual async Task UpdateAsync(T item) { await this.Client.UpdateAsync<T>(item.Id, x => x.Index(this.IndexName).Doc(item)); } public virtual async Task<Tuple<int, IList<T>>> QueryAsync(int page, int limit) { var query = await this.Client.SearchAsync<T>(x => x.Index(this.IndexName) .From((page -1) * limit) .Size(limit)); return new Tuple<int, IList<T>>(Convert.ToInt32(query.Total), query.Documents.ToList()); } }
有了上面的基類,我們再去實現具體的Repository就方便的多啦。
實現AppLogRepository
下面我們可以很方便的實現一個AppLogRepository啦,是不是很方便!
public class AppLogRepository : ElasticRepositoryBase<AppLog>, IAppLogRepository { public AppLogRepository(IElasticProxy elasticProxy) : base(elasticProxy) { } protected override string IndexName => "app-logs"; }
定義依賴注入
下面我們把要用到的ElasticProxy和Repository注入到ServiceCollection中,後面Controller就可以用了!這裏我們在Infrastructure中實現一個擴展方法,便於在Program.cs中使用。
public static class InfraServiceExtensions { public static void AddApplicationInfrastructure(this IServiceCollection services) { services.AddSingleton<IElasticProxy, ElasticProxy>(); services.AddSingleton<IProductRepository, ProductRepository>(); services.AddSingleton<IAppLogRepository, AppLogRepository>(); } }
然後在Program.cs中使用這個擴展方法:
...... // Add infra services / repositories builder.Services.AddApplicationInfrastructure();
實現AppLogController
下面我們就可以快速實現一個Controller來調用Repository實現ES的CRUD操作啦:
[ApiController] [Route("[controller]")] public class AppLogController : ControllerBase { private readonly IAppLogRepository _appLogRepository; public AppLogController(IAppLogRepository appLogRepository) { _appLogRepository = appLogRepository; } [HttpGet] public async Task<IActionResult> QueryAsync(int page = 1, int limit = 10) { var result = await _appLogRepository.QueryAsync(page, limit); return Ok(new { total = result.Item1, items = result.Item2 }); } [HttpPost] public async Task<IActionResult> AddAsync([FromBody] AppLog log) { await _appLogRepository.AddAsync(log); return Ok("Success"); } [HttpPut] public async Task<IActionResult> UpdateAsync([FromBody] AppLog log) { await _appLogRepository.UpdateAsync(log); return Ok("Success"); } [HttpDelete] public async Task<IActionResult> DeleteAsync([Required] string id) { await _appLogRepository.DeleteAsync(id); return Ok("Success"); } }
快速測試
下面我們就可以打開Swagger來做一個測試啦:
通過Post方式創建一個applog,然後我們通過Get方式查詢一下結果如下:
這時我們還可以通過在ES中通過Query DSL來驗證一下:
GET app-logs/_search
查詢結果如下圖所示:
至於Put和Delete就留給你去驗證吧。
小結
本篇,我們瞭解瞭如何在ASP.NET 6應用中集成ElasticSearch,並通過示例瞭解瞭如何做CRUD操作,並通過一定的封裝提高了複用性。
下一篇我們瞭解下如何在ASP.NET 6應用對ES中的數據進行查詢 和 聚合。
示例代碼
Github:https://github.com/Coder-EdisonZhou/ElasticSamples
參考資料
博客園,包子wxl,《ElasticSearch使用系列-.NET6對接ES》
CSDN,阿星Plus,《.NET Core下使用ES》
CSDN,風神.NET,《如何在ASP.NET Core中集成ES》
極客時間,阮一鳴,《ElasticSearch核心技術與實戰》