如何在 ASP.NET Core 中集成 ElasticSearch
本文來自:https://www.blexin.com/en-US/Article/Blog/How-to-integrate-ElasticSearch-in-ASPNET-Core-70
我敢打賭,您肯定會被要求向Web應(yīng)用程序中添加高級搜索功能,而且通常是全文的類似Google的搜索。
在技術(shù)電子商務(wù)的開發(fā)過程中,我們被要求允許用戶對產(chǎn)品進行高級研究,以便他們可以高效,完全地找到所需的內(nèi)容。
我們基于對象的所有字段上給定字符串的搜索嘗試了自定義搜索的實現(xiàn)。為了優(yōu)化時間,我們嘗試在服務(wù)和數(shù)據(jù)庫級別之間添加一個緩存層,以避免對數(shù)據(jù)庫造成過多壓力,但是我們對結(jié)果不滿意。然后,我們在市場上搜索了可以滿足我們需求的第三方產(chǎn)品,經(jīng)過深入分析,我們選擇采用ElasticSearch:一個基于REST協(xié)議的,可管理研究和分析的分布式,易于適應(yīng)的搜索引擎同樣,也方便了數(shù)據(jù)的外推和轉(zhuǎn)換。
具體來說,我們正在談?wù)摶贏pache Lucene的開源全文搜索引擎,該引擎可用于管理文檔的索引和研究。讓我們嘗試了解基本概念。
ElasticSearch將數(shù)據(jù)存儲在一個或多個索引中。ES的索引與SQL DB的索引非常相似,因為我們使用它來存儲和讀取文檔。
文檔是ElasticSearch世界的主要實體。它由一組具有名稱和一個或多個值的字段組成。每個文檔可能具有一組字段,并且沒有給出任何架構(gòu)或定義的結(jié)構(gòu)。這只是一個JSON對象。
所有文檔在存儲之前都經(jīng)過分析。這種分析過程(稱為映射)是通過過濾數(shù)據(jù)內(nèi)容(例如,刪除HTML標簽)并將其標記化來執(zhí)行的,以便將文檔拆分為標記。
ElasticSearch中的每個文檔都有一個類型。這樣就可以將各種文檔類型存儲在同一索引上,并為幾種類型獲取幾種映射。
ElasticSearch服務(wù)器的單個實例稱為Node。在很多情況下,單個節(jié)點就足夠了,但是有時您需要管理故障,或者您有太多數(shù)據(jù)無法使用單個節(jié)點進行管理。在這種情況下,您可以使用多節(jié)點集群,這是一組協(xié)同工作的節(jié)點來管理比單個實例無法處理的更大的負載。您可以配置群集,以便即使某些節(jié)點不可用,也可以保證搜索和管理功能。
為了使群集正常運行,ElasticSearch將數(shù)據(jù)分布在Apache Lucene的多個物理索引上。這些索引稱為“ 碎片”,而擴展過程稱為“ 碎片”。ElasticSearch自動管理分片,因此最終用戶似乎只是一個大索引。
副本是分片的副本,可用于以原始分片的相同模式進行查詢。
副本可減輕無法處理所有請求的單個節(jié)點上的負載,并提供更高的數(shù)據(jù)安全性,因為如果您丟失了原始分片中的數(shù)據(jù),則可以在副本上對其進行恢復(fù)。
ElasticSearch收集了大量有關(guān)集群狀態(tài),索引設(shè)置的信息,并將它們存儲到網(wǎng)關(guān)中。
從結(jié)構(gòu)上講,ElasticSearch基于一些簡單的關(guān)鍵概念:
- 默認設(shè)置和值使得默認配置足以立即使用ElasticSearch。
- 它以分布式方式工作。節(jié)點自動成為集群的一部分,并且在設(shè)置過程中,節(jié)點嘗試加入集群。
- 沒有SPOF的P2P體系結(jié)構(gòu)(單點故障)。節(jié)點自動連接到群集的其他計算機以更改數(shù)據(jù)和相互監(jiān)視;
- 只需在集群中添加新節(jié)點,就可以輕松地進行擴展,無論是在數(shù)據(jù)量上還是在容量上。
- 在組織索引中的數(shù)據(jù)方面沒有任何限制。允許用戶修改數(shù)據(jù)模型而不會對搜索產(chǎn)生任何影響;
- NRT(近實時)搜索和版本控制。由于其分布式特性,無法避免延遲和位于不同節(jié)點上的數(shù)據(jù)之間的差異。因此,它提供了版本控制機制。
當ElasticSearch節(jié)點啟動時,它使用多播(或單播,如果已配置)來查找同一集群中的其他節(jié)點并連接到它們。
在群集中,選擇一個節(jié)點作為主節(jié)點。該節(jié)點負責管理集群狀態(tài)和將分片分配給節(jié)點的過程。主節(jié)點讀取集群狀態(tài),并在需要時啟動恢復(fù)模式,該模式允許知道哪些分片可用,并指定其中一個作為主分片。這樣,即使群集沒有可用的全部資源,它也似乎可以正常工作。然后,主節(jié)點查找重復(fù)的分片,并將其作為副本處理。
在標準運行期間,主節(jié)點檢查所有可用節(jié)點是否正常工作。如果其中之一在配置的時間范圍內(nèi)不可用,則將該節(jié)點視為已損壞,并運行容錯過程。容錯的主要活動是平衡已損壞節(jié)點的群集和碎片,并分配一個負責這些碎片的新節(jié)點。然后,對于每個主分片丟失,將定義一個在可用副本之間選擇的新主分片。
如前所述,ElasticSearch提供了一些API REST,可供每個能夠發(fā)送HTTP請求和接收HTTP響應(yīng)的系統(tǒng)使用(大多數(shù)開發(fā)框架的所有瀏覽器和庫)。
ElasticSearch請求由一些包含的已定義URL發(fā)送。最終是JSON主體。響應(yīng)也是JSON文檔。
ElasticSearch提供了四種索引數(shù)據(jù)的方式。
1.索引API:它允許將文檔發(fā)送到已定義的索引;
2.批量API:它允許通過HTTP協(xié)議發(fā)送多個文檔;
3.UDP批量API:它允許通過任何協(xié)議發(fā)送多個文檔(更快但更不可靠);
4.插件:在節(jié)點上執(zhí)行,它們從外部系統(tǒng)獲取數(shù)據(jù)。
重要的是要記住,索引只是在主分片上而不是在其副本上,因此,如果將索引請求發(fā)送到不包含主分片或可能包含其副本的節(jié)點,則該請求將轉(zhuǎn)發(fā)到主分片。
使用Query API執(zhí)行搜索。使用查詢DSL(基于JSON的語言來構(gòu)建復(fù)雜的查詢),可以:
- 使用各種類型的查詢,包括簡單查詢,短語,范圍,布爾值,空間查詢和其他查詢;
- 通過組合簡單查詢來構(gòu)建復(fù)雜查詢;
- 通過排除不符合選定條件的文檔而不影響其分數(shù)來過濾文檔;
- 查找與其他文件相似的文件;
- 查找給定短語的建議或更正;
- 查找與給定文檔匹配的查詢。
搜索不是一個單階段的簡單過程,但是,通??梢詫⑵浞譃閮蓚€階段:scatter(分散),在其中查詢索引的所有相關(guān)分片;gather(收集),在其中收集,處理和排序所有寶貴的結(jié)果。
弄臟你的手!
ES提供了云和本地兩種使用方式。如果要在Windows計算機上安裝它,則需要具有Java虛擬機的更新版本(https://www.elastic.co/support/matrix#matrix_jvm),然后可以從ElasticSearch下載中下載一個zip文件。頁面(https://www.elastic.co/downloads/elasticsearch)并將其提取到磁盤上的文件夾中,例如C:\ Elasticsearch。
要執(zhí)行它,您可以運行C:\ Elasticsearch \ bin \ elasticsearch.bat。
如果要將ElasticSearch用作服務(wù),以便可以使用Windows工具啟動或停止它,則需要在文件C:\ Elasticsearch \ config \ jvm.options中添加一行。
對于32位系統(tǒng),您必須鍵入-Xss320k*,對于64位系統(tǒng)-Xss1m。*
更改此設(shè)置后,您必須打開命令提示符或Powershell并執(zhí)行
C:\ Elasticsearch \ bin \ elasticsearch-service.bat
??捎玫拿畎?install, remove, start, stop 和manager.。
要創(chuàng)建服務(wù),我們必須輸入:
C:\ Elasticsearch \ bin \ elasticsearch-service.bat install
要管理服務(wù),我們鍵入:
C:\ Elasticsearch \ bin \ elasticsearch-service.bat manager
器,該管理器打開Elastic Service Manager,這是一個GUI,可通過該GUI進行有關(guān)服務(wù)的自定義設(shè)置并管理其狀態(tài)。
默認cluster.name和node.name是elasticsearch分別和你的主機名。如果您打算繼續(xù)使用該群集或添加更多節(jié)點,則最好通過在elasticsearch.yml
文件中對其進行修改來將這些默認值更改為唯一名稱。
我們可以通過瀏覽http:// localhost:9200 /來驗證ElasicSearch的正確執(zhí)行。如果一切正常,我們將得到以下結(jié)果:
為了實現(xiàn)基于.NET Core的解決方案,我們使用了NEST軟件包,可以通過以下命令安裝該軟件包:
- dotnet add package NEST
NEST允許我們在索引和搜索文檔以及節(jié)點和分片的管理中本地使用所有ElasticSearch功能。為了管理NEST插件,我們創(chuàng)建了ElasticsearchExtensions類:
- public static class ElasticsearchExtensions
- {
- public static void AddElasticsearch(this IServiceCollection services, IConfiguration configuration)
- {
- var url = configuration["elasticsearch:url"];
- var defaultIndex = configuration["elasticsearch:index"];
- var settings = new ConnectionSettings(new Uri(url))
- .DefaultIndex(defaultIndex);
- AddDefaultMappings(settings);
- var client = new ElasticClient(settings);
- services.AddSingleton(client);
- CreateIndex(client, defaultIndex);
- }
- private static void AddDefaultMappings(ConnectionSettings settings)
- {
- settings
- DefaultMappingFor<Product>(m => m
- .Ignore(p => p.Price)
- .Ignore(p => p.Quantity)
- .Ignore(p => p.Rating)
- );
- }
- private static void CreateIndex(IElasticClient client, string indexName)
- {
- var createIndexResponse = client.Indices.Create(indexName,
- index => index.Map<Product>(x => x.AutoMap())
- );
- }
- }
在其中我們找到對象的配置和映射,在本例中為Product類。在此類中,我們決定忽略在索引階段存儲價格,數(shù)量和評級。通過以下指令在Startup.cs中調(diào)用此類:
- public void ConfigureServices(IServiceCollection services)
- {
- // ...
- services.AddElasticsearch(Configuration);
- }
這使我們能夠在啟動時加載的所有設(shè)置,在修改它們elasticsearch的appsettings.json文件,在其中我們插入如下一行:
- "elasticsearch": {
- "index": "products",
- "url": "http://localhost:9200/"
- }
索引表示選擇用來存儲文檔的默認索引,而url是我們的ElasticSearch實例的地址。我們的產(chǎn)品對象定義如下:
- public class Product
- {
- public int Id { get; set; }
- public string Ean { get; set; }
- public string Name { get; set; }
- public string Description { get; set; }
- public string Brand { get; set; }
- public string Category { get; set; }
- public string Price { get; set; }
- public int Quantity { get; set; }
- public float Rating { get; set; }
- public DateTime ReleaseDate { get; set; }
- }
如前所述,可以分別或在列表中為產(chǎn)品建立索引。在我們的產(chǎn)品服務(wù)中,我們實現(xiàn)了兩種方式:
- public async Task SaveSingleAsync(Product product)
- {
- if (_cache.Any(p => p.Id == product.Id))
- {
- await _elasticClient.UpdateAsync<Product>(product, u => u.Doc(product));
- }
- else
- {
- _cache.Add(product);
- await _elasticClient.IndexDocumentAsync(product);
- }
- }
- public async Task SaveManyAsync(Product[] products)
- {
- _cache.AddRange(products);
- var result = await _elasticClient.IndexManyAsync(products);
- if (result.Errors)
- {
- // the response can be inspected for errors
- foreach (var itemWithError in result.ItemsWithErrors)
- {
- _logger.LogError("Failed to index document {0}: {1}",
- itemWithError.Id, itemWithError.Error);
- }
- }
- }
- public async Task SaveBulkAsync(Product[] products)
- {
- _cache.AddRange(products);
- var result = await _elasticClient.BulkAsync(b => b.Index("products").IndexMany(products));
- if (result.Errors)
- {
- // the response can be inspected for errors
- foreach (var itemWithError in result.ItemsWithErrors)
- {
- _logger.LogError("Failed to index document {0}: {1}",
- itemWithError.Id, itemWithError.Error);
- }
- }
- }
在這里我們使用_cache數(shù)組來進一步緩存產(chǎn)品列表。對于多模式,我們也實現(xiàn)了批量版本,這使我們能夠在更短的時間內(nèi)索引大量文檔,并且我們已經(jīng)處理了日志插入中的任何錯誤。
請注意,SaveSingleAsync方法通過檢查緩存數(shù)組來管理文檔的插入和修改。
對于文檔刪除,我們實現(xiàn)了DeleteAsync方法:
- public async Task DeleteAsync(Product product)
- {
- await _elasticClient.DeleteAsync<Product>
- (product);
- if (_cache.Contains(product))
- {
- _cache.Remove(product);
- }
- }
GetSearchUrl方法允許我們獲取用于管理頁面調(diào)度的URL。出于開發(fā)目的,我們實現(xiàn)了ReIndex方法,該方法允許我們刪除索引上的所有文檔,然后一次又一次地導入它們。這對于導入現(xiàn)有和未加載文檔的列表很有用。
- //Only for development purpose
- [HttpGet("/search/reindex")]
- public async Task<IActionResult>ReIndex()
- {
- await _elasticClient.DeleteByQueryAsync<Product>(q => q.MatchAll());
- var allProducts = (await _productService.GetProducts(int.MaxValue)).ToArray();
- foreach (var product in allProducts)
- {
- await _elasticClient.IndexDocumentAsync(product);
- }
- return Ok($"{allProducts.Length} product(s) reindexed");
- }
出于示例目的,我們創(chuàng)建了一個界面,該界面允許我們通過Bogus插件添加N個動態(tài)生成的產(chǎn)品,并管理產(chǎn)品的CRUD。運行項目后,我們將看到以下屏幕:
例如,如果我們嘗試將10種產(chǎn)品添加到索引中,在文本框中輸入10,然后單擊“ 導入文檔”按鈕,則可以使用搜索框查看結(jié)果,也可以直接從瀏覽器中瀏覽到http頁面:// localhost:9200 / products / _search,我們將在其中得到這樣的結(jié)果:
本文中使用的代碼可在此處獲得[2]。
References
[1] 查看原文: https://www.blexin.com/en-US/Article/Blog/How-to-integrate-ElasticSearch-in-ASPNET-Core-70
[2] 在此處獲得: https://github.com/enricobencivenga/ElasticSearch