.NetCore下ES查詢驅動 PlainElastic .Net 升級官方驅動 Elasticsearch .Net

来源:https://www.cnblogs.com/xboo/archive/2019/07/23/11232661.html
-Advertisement-
Play Games

1.背景 由於歷史原因,筆者所在的公司原有的ES查詢驅動採用的是 PlainElastic.Net, 經過詢問原來是之前PlainElastic.Net在園子里文檔較多,上手比較容易,所以最初作者選用了該驅動,而發佈也由於歷史原因都部署在 windows 伺服器上,基於 .NET Framework ...


1.背景

由於歷史原因,筆者所在的公司原有的ES查詢驅動採用的是 PlainElastic.Net, 經過詢問原來是之前PlainElastic.Net在園子里文檔較多,上手比較容易,所以最初作者選用了該驅動,而發佈也由於歷史原因都部署在 windows 伺服器上,基於 .NET Framework開發。

後來由於遷移 .NET CORE 平臺的需要,對代碼進行了升級,同時部署平臺也遷移至 CentOS7 伺服器,升級過程比較順利,由於沒有使用特殊API,所以幾乎沒有對業務代碼做更多的修改,同時測試階段由於沒有多餘的機器,仍然放在了原有Windows伺服器上做的測試,一切都沒有問題,完美上線。

事發突然,某天接到運維部門反饋,部署查詢服務的機器突然出現 TCP 連接數超高的問題,同時這台機器其他的TCP服務也無法建立新的連接,但已經建立的連接不受影響。聯想到 ElasticSearch 查詢服務是基於HTTP 請求的,腦子裡馬上聯想到 .NET Core 下 HttpClient 如果每次訪問都創建新實例,則會每次都建立新的TCP連接,而 Linux 對已釋放埠回收的時間視窗,會導致在高併發情況下,客戶端機器埠占用持續增加,同時被調用服務端連接數也會持續增加。

基於此猜測,立馬去扒了一下PlainElastic.Net源代碼:

源碼地址:https://github.com/Yegoroff/PlainElastic.Net/blob/master/src/PlainElastic.Net/Connection/ElasticConnection.cs
果然如猜測的那樣,每次都創建了新的 HttpWebRequest 實例,看了作者的最後維護時間也已經是3年前了,可能是後來官方驅動日趨完善,作者也便停止了維護。

既然如此,那麼讓我們看下官方最新驅動源碼是否如我們想象,是基於HttpClientFactory來解決這個問題的?

源碼地址:https://github.com/elastic/elasticsearch-net/blob/master/src/Elasticsearch.Net/Connection/HttpConnection.cs

上述代碼看來,官方驅動並非是採用微軟官方建議的 HttpClientFactory ,而是官方底層自己維護的一個線程安全的字典來管理 HttpClient 實例池,雖是自己實現,但效果一樣:相同地址的請求,是鏈接復用的,這樣就解決不斷開啟 TCP 連接的問題。

問題找到,立馬進行驅動升級:

2.驅動升級

說明: ElasticSearch.Net官方驅動地址:https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/index.html

官方驅動分為 Low Level Client 和 NEST(Heigh Level Client),其中Low Level Client 僅僅做了最基本的封裝,幾乎等價於HTTP原生調用,帶來了極大的靈活性的同時,也帶來使用成本,而對於開發人員來說使用 NEST 提供的更加高級的API,可以更加快速的進行開發工作,也同時可以利用到 .NET 所提供的各種語法糖,比如 => 表達式。

話不多說,看示例代碼:

實例創建

public ElasticService()
{
    var uris = new Uri[] { new Uri("http://172.17.78.111:9200"), new Uri("http://172.17.78.112:9200") }; //支持多個節點
    var connectionPool = new SniffingConnectionPool(uris);
    var settings = new ConnectionSettings(connectionPool).DefaultIndex("testindex");//註意index不可以大寫
    settings.BasicAuthentication("", ""); //設置賬號密碼,沒有可以跳過
    this._client = new ElasticClient(settings);
}

插入待測試數據

public class People 
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public DateTime Birthday { get; set; }
    public bool Gender { get; set; }
    public string Address { get; set; }
    public DateTime CreateTime { get; set; } = DateTime.Now;
}

//批量插入
public async Task<IBulkResponse> AddPeopleAsync(People[] peoples)
{
    var descriptor = new BulkDescriptor();
    foreach (var p in peoples)
    {
        var response = await _client.IndexDocumentAsync(p);
        descriptor.Index<People>(op => op.Document(p));
    }
    return await _client.BulkAsync(descriptor);//批量插入
}

多查詢條件拼接

public QueryContainer BuildQueryContainer(SearchCondition condition)
{
    var queryCombin = new List<Func<QueryContainerDescriptor<People>, QueryContainer>>();
    if (!string.IsNullOrEmpty(condition.Name))
        queryCombin.Add(mt => mt.Match(m => m.Field(t => t.Name).Query(condition.Name))); //字元串匹配

    if (condition.Age.HasValue)
        queryCombin.Add(mt => mt.Range(m => m.Field(t => t.Address).GreaterThanOrEquals(condition.Age))); //數值區間匹配

    if (!string.IsNullOrEmpty(condition.Address))
        queryCombin.Add(mt => mt.MatchPhrase(m => m.Field(t => t.Address).Query(condition.Address))); //短語匹配

    if (!condition.Gender.HasValue)
        queryCombin.Add(mt => mt.Term(m => m.Field(t => t.Gender).Value(condition.Gender)));//精確匹配

    return Query<People>.Bool(b => b
        .Must(queryCombin)
        .Filter(f => f
            .DateRange(dr => dr.Field(t => t.CreateTime) //時間範圍匹配
                .GreaterThanOrEquals(DateMath.Anchored(condition.BeginCreateTime.ToString("yyyy-MM-ddTHH:mm:ss")))
                .LessThanOrEquals(DateMath.Anchored(condition.EndCreateTime.ToString("yyyy-MM-ddTHH:mm:ss"))))));
}

提示:Match 和 MatchPhrase 的區別,例如對於"長寧區"

  1. Match 會將"長寧區"進行分詞匹配,例如只要包含"區"的數據(比如靜安區),也會被查詢命中
  2. MatchPhrase 則可以理解為短語匹配,只有當數據包含“長寧區”完整短語的數據,才會被查詢命中

增加分頁查詢介面

public async Task<PagedResult<People[]>> QueryPeopleAsync(SearchCondition condition, int pageIndex, int pageSize)
{
    var query = this.BuildQueryContainer(condition);
    var response = await this._client.SearchAsync<People>(s => s
            .Index("testindex")
            .From(pageIndex * pageSize)
            .Size(pageSize)
            .Query(q => query)
            .Sort(st => st.Descending(d => d.CreateTime)));

    if (response.ApiCall.Success)
    {
        return new PagedResult<People[]>
        {
            PageIndex = pageIndex,
            PageSize = pageSize,
            Total = response.Total,
            ReturnObj = response.Hits.Select(s => s.Source).ToArray()
        };
    }

    return new PagedResult<People[]> { IsSuccess = false };
}

編寫單元測試

[TestMethod]
public async Task QueryPeopleTest()
{
    var condition = new SearchCondition
    {
        Address="長寧區",
        BeginCreateTime = DateTime.Now.AddDays(-1),
        EndCreateTime = DateTime.Now
    };

    var result = await this._elasticService.QueryPeopleAsync(condition, 0, 3);
    Assert.IsTrue(result.IsSuccess);
}

利用 Wireshark 抓包分析HTTP調用細節

將抓包的數據轉換為HTTP流,查看請求細節:

提示:通過wireshark抓包是排查錯誤很有效的方式,有時候通過查詢文檔進行分析,還不如先抓包查看請求數據來得直接,同時可以將抓包數據放在Kabana所提供的 Dev Tools中驗證自己的想法。

利用 Kibana 提供的 Dev Tools 驗證/測試 查詢條件

3.總結

從.NET Framework 平臺轉向 .Net Core 平臺,其實不僅僅是開發框架的升級,或者從 Windows 轉向 Linux 的遷移,而是需要我們有更多的開源思維,即:

  1. 由於會使用到更多的三方組件,開發人員需要更多關註社區的變化
  2. 開源代碼,意味著開發人員可以並且需要更多關註源代碼的底層實現

本文示例代碼地址:https://github.com/xBoo/articles/tree/master/src/ElasticSearchNetDemo


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文介紹如何利用Python+uiautomator2 每日自動賺取支付寶積分。 支付寶的積分有啥用?誘惑誘惑你: 可以兌換視頻網站的VIP會員。 可以兌換各種優惠券。 可以在年底活動中兌換蘋果手機。 其他,一言難盡... 比如,我喜歡買知識課堂的課程。(知識付費時代,我用積分買知識) 好了,有了動 ...
  • 一、 cookie 1. 定義:保存在瀏覽器本地上的一組組鍵值對 2. 特點: 由伺服器讓瀏覽器進行設置的 瀏覽器保存在瀏覽器本地 下次訪問時自動攜帶 3. 應用: 登錄 保存瀏覽習慣 簡單的投票 4. 使用cookie的原因:因為HTTP是無狀態的,用cookie來保存狀態 5. 在django中 ...
  • 本文介紹的Java規則的說明分為3個主要級別,中級是平時開發用的比較多的級別,在今後將陸續寫出其他的規則。遵守了這些規則可以提高程式的效率、使代碼又更好的可讀性等。 一、在finally方法里關掉input或者output資源 方法體裡面定義了input或者output流的話,需要在finally里 ...
  • T1 足球聯賽 題目 【題目描述】 巴蜀中學新一季的足球聯賽開幕了。足球聯賽有n只球隊參賽,每賽季,每隻球隊要與其他球隊各賽兩場,主客各一場,贏一場得3分,輸一場不得分,平局兩隻隊伍各得一分。 英勇無畏的小鴻是機房的主力前鋒,她總能在關鍵時刻踢出一些匪夷所思的妙球。但是很可惜,她過早的燃燒完了她的職 ...
  • hhh 為年薪20萬加油ヾ(◍°∇°◍)ノ゙ 一、變數:(變數的命名規則:一般使用字母開頭,可以使用下劃線連接,以及數字) 正確的變數命名示範: (儘量使用容易理解什麼用途的詞語) a1 name_Li name2 錯誤的變數示例: 1a a=1 print(a) b='年薪百萬不是夢' print ...
  • 一個可以沉迷於技術的程式猿,wx加入加入技術群:fsx641385712 ...
  • import os def file_handler(backend_data,res=None,type='fetch'): # 查詢功能 if type == 'fetch': with open('test_new.txt','r') as read_f: ret = [] ... ...
  • 介紹 使用函數式編程來豐富面向對象編程的想法是陳舊的。將函數編程功能添加到面向對象的語言中會帶來面向對象編程設計的好處。 一些舊的和不太老的語言,具有函數式編程和麵向對象的編程: 例如,Smalltalk和Common Lisp。 最近是Python或Ruby。 面向對象編程中模擬的函數式編程技術 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...