[11]elasticsearch源碼深刻分析——文檔(document)的落地

本篇爲elasticsearch源碼分析系列文章的第十一篇,本篇開始進入索引有關操做的講解。之後的若干篇咱們會連續討論文檔的建立,檢索,更新,刪除,版本控制等一系列內容。程序員

文檔

ElasticSearch存儲系統中的實體叫作文檔,document。若是用關係型數據庫來類比的話,一個文檔至關於數據庫中的一行記錄。ElasticSearch中的文檔有個特色,相同字段必須是相同的類型,也就是說全部包含title字段的文檔,title字段類型都必須同樣,要麼同爲string,要麼同爲int。數據庫

文檔由多個字段組成,每一個字段的類型能夠是,文本,數值,日期,還能夠是字符串數組這種複雜的類型。字段類型在ElasticSearch中很是重要,它涉及到各類分析和排序操做如何被執行的信息。Elastic官方推薦咱們使用Mapping映射來干預字段的類型。與關係型數據庫不一樣,ElasticSearch不須要有固定的結構,每一個文檔能夠有不一樣的字段,此外,在程序開發期間,沒必要肯定有哪些字段。數組

文檔的類型

在ElasticSearch中,文檔類型可讓程序員輕鬆的區分單個索引中的不一樣對象。每一個文檔能夠有不一樣的結構,但在實際生產環境中咱們仍是推薦將文檔中的類型詳細化,這樣對之後的開發會有很大的幫助。app

文檔類型的映射

上面提到的映射,指的是ElasticSearch在映射中存儲有關字段的信息,這種類型信息就是映射Mapping。每一個文檔類型都有本身的映射,即便在初始化時沒有提早定義。在涉及到全文搜索和倒排索引的內容中,會有對文檔分析的過程,在這個過程當中每一個字段都必須根據不一樣類型做相應的分析。舉例來講,對數值字段和文本字段的分析確定是不一樣的分析過程,數字的分析就不該該是按照字母的排序來分析。elasticsearch

使用ElasticSearch的ResultAPI來新建文檔

在ElasticSearch中,全部文檔都是數據,全部數據都有定義好的索引和類型。如今咱們經過一個比較常見的例子來創建文檔:源碼分析

建立文檔

上面操做的意思是,咱們創建了一個名爲article的索引和名爲computer的類型,文檔的標示符爲1ui

若是一切正常,那麼這種使用RESTfulAPI的建立方式會返回一個JSON響應,與以下輸出相似:spa

文檔建立成功

前面的相應包含了操做狀態的信息,顯示了建立的文檔放在什麼地方,還包含了文檔的惟一標示符**_id和當前版本_version**的信息。每次ElasticSearch的更新版本都會自動遞增。3d

並且ElasticSearch在建立文檔時,若是沒指定文檔標示符,那麼這個文檔的標示符會被自動建立。版本控制

自動建立文檔標示符

這都是怎麼作到的呢?咱們會在下一節從源碼的角度解釋。

ElasticSearch源碼如何新建文檔

在之前文章中強調的Node實例化的過程當中,加載了ActionModule這個模塊,這個模塊是接收客戶端發送的RESTful請求的的模塊,ActionModule的加載以下:

ActionModule actionModule = new ActionModule(false, settings, clusterModule.getIndexNameExpressionResolver(), settingsModule.getIndexScopedSettings(), settingsModule.getClusterSettings(), settingsModule.getSettingsFilter(), threadPool, pluginsService.filterPlugins(ActionPlugin.class), client, circuitBreakerService, usageService);

在加載完了ActionModule後,會經過ActionModule的方法**initRestHandlers()**來初始化HTTP處理程序,這個handler就能解析客戶端經過http協議發送到ElasticSearch集羣中的RESTful請求。

加載RestIndexActionindex處理器,

registerHandler.accept(new RestIndexAction(settings, restController))

以下圖所示,註冊不一樣的REST處理程序路徑,以用來不一樣的匹配請求。

不一樣的索引請求

能夠看到控制器匹配路徑中,有index,type和id,若是不指定id,則id會被自動建立,並且不指定id必須用POST方法來發送請求。

由於ElasticSearch中的Controller底層都是Netty實現的。因此在端口綁定後,Netty4HttpChannel會去監聽端口收到的http請求。在ElasticSearch的Controller接收到Netty4HttpChannel轉發的請求後,會調用RestIndexAction中的方法prepareRequest()。該方法返回RestChannelConsumer類型的實例,該實例是虛擬類BaseRestHandler中的Functional接口。閱讀這個接口的定義的方法,能夠知道ElasticSearch中的REST請求是經過準備一個表示通道的請求執行的通道消費者(a channel consumer)來處理的。

接收到請求後開始構建IndexRequest,這個實例做用是將JSON類型的文檔轉換爲一個特定的和可搜索的索引。

IndexRequest回首先取得RestRequest中的三個構造實例必須的參數:

  • index:文檔的索引
  • type:文檔的類型
  • id:文檔指定的標識

而後在依次取得一些附加參數:

  • routing:控制分片的路由請求。使用這個值來哈希的分片,而不是id。
  • parent:設置document的父id。
  • pipeline:在執行索引document前,設置攝取管道(ingest pipeline)
  • source:設置document索引的字節形式。
  • timeout:超時時間
  • refresh:解析刷新策略
  • version_type:設置版本類型
  • op_type:字符串,用來表示是索引數據仍是新建數據

參數詳情以下圖:

image.png

這參數都是NodeClient在索引文檔時候須要用到的數據,NodeClient在Node初始化時候就加載完成,他是用來在本地節點上執行操做的模擬客戶端。

方法prepareRequest最後返回channel -> client.index(indexRequest, new RestStatusToXContentListener<>(channel, r -> r.getLocation(indexRequest.routing()))),由於該方法須要返回RestChannelConsumer類型的返回值,因此改寫成jdk7版本易於理解的代碼版本以下圖所示:

返回代碼

該段代碼中最重要的就是NodeClient的index()方法,此方法的關鍵是新建了一個Task,這個Task包含了id,type,action,description,parentTask,startTime等信息。

該task在老版本會被TransportIndexAction處理,可是6.0版本後TransportBulkAction已經取代了TransportIndexAction。task會被當作參數送入TransportBulkAction的doExecute方法中,另外兩個參數是BulkRequest和ActionListener

void doExecute(Task task, BulkRequest bulkRequest, ActionListener<BulkResponse> listener)
複製代碼

BulkRequest中包含了該文檔存儲的信息,而ActionListener則用來監聽action的響應或失敗,用以作回調操做。

doExecute方法主要作了如下操做:

  • 收集請求中的全部索引
  • 過濾掉不存在的索引,同時創建一個咱們沒法建立的索引圖。判斷不存在的索引和沒法建立的索引主要是看索引是否有別名
  • 若是有遺漏的索引,則建立缺乏的全部索引。注意在全部的建立完成後開始批量處理數據

而後執行TransportBulkAction類的executeBulk方法,完成數據的落地。

相關文章
相關標籤/搜索