elasticsearch 源碼 創建索引

elasticsearch 源碼 創建索引

創建索引過程,當我們客戶端提交一個創建索引請求時,之前提到了es的transport模塊,在處理請求時,會將請求分發到對應的TransportRequestHandler,而創建索引的入口就是TransportHandler對象,這個對象對應的類是TransportCreateIndexAction的內部類,而TransportCreateIndexAction的這個內部類繼承自父類TransportMasterNodeOperationAction。

//TransportMasterNodeOperationAction.java
private class TransportHandler extends BaseTransportRequestHandler<Request> {

    @Override
    public Request newInstance() {
        return newRequest();
    }

    @Override
    public String executor() {
        return ThreadPool.Names.SAME;
    }  // 使用的是same,請求不是交由線程池執行

    @Override
    public void messageReceived(final Request request, final TransportChannel channel) throws Exception {
        // we just send back a response, no need to fork a listener
        request.listenerThreaded(false);
        execute(request, new ActionListener<Response>() {
            @Override
            public void onResponse(Response response) {
                try {
                    channel.sendResponse(response);
                } catch (Throwable e) {
                    onFailure(e);
                }
            }

            @Override
            public void onFailure(Throwable e) {
                try {
                    channel.sendResponse(e);
                } catch (Exception e1) {
                    logger.warn("Failed to send response", e1);
                }
            }
        });
    }
}

服務端在接收到創建索引請求時,會調用TransportHandler的messageReceived方法,進而調用execute方法。可以看到ActionListener 封裝了response,讓調用者可以執行調用OnResponse方法來響應,或者調用OnFailure來響應失敗。而後

//TransportMasterNodeOperationAction.java
public void execute(Request request, ActionListener<Response> listener) {
    request.listenerThreaded(true);
    super.execute(request, listener);
}

//TransportAction.java
public void execute(Request request, ActionListener<Response> listener) {
    if (request.listenerThreaded()) {
        //對ActionListener 進行封裝ThreadedActionListener    封裝線程池
        listener = new ThreadedActionListener<Response>(threadPool, listener, logger);
    }
    ActionRequestValidationException validationException = request.validate();
    if (validationException != null) {
        listener.onFailure(validationException);
        return;
    }
    try {
        doExecute(request, listener);
    } catch (Throwable e) {
        logger.trace("Error during transport action execution.", e);
        listener.onFailure(e);
    }
}

方法調用,對ActionListener進行了封裝,得到一個ThreadedActionListener對象,這個對象繼承自ActionListener,不過他的onResponse方法和onFailure方法中響應處理Response的邏輯是由generic類型的線程池來執行。

 public void onResponse(final Response response) {
        try {
            threadPool.generic().execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        listener.onResponse(response);
                    } catch (Throwable e) {
                        listener.onFailure(e);
                    }
                }
            });
        } catch (EsRejectedExecutionException ex) {
            logger.debug("Can not run threaded action, exectuion rejected 
            try {
                listener.onResponse(response);
            } catch (Throwable e) {
                listener.onFailure(e);
            }
        }
    }

    @Override
    public void onFailure(final Throwable e) {
        try {
            threadPool.generic().execute(new Runnable() {
                @Override
                public void run() {
                    listener.onFailure(e);
                }
            });
        } catch (EsRejectedExecutionException ex) {
            listener.onFailure(e);
        }
    }

上邊調用了doExecute方法,最終會調用innerExecute方法。這個方法比較長,不貼代碼了說下具體邏輯
1.獲取集羣信息ClusterState,進而獲取到所有的節點信息
2.判斷本節點是否是master節點,若是mater節點則使用線程池調用masterOperation方法,使用的是same類型線程池,否則執行3
3.不是master節點,判斷是否有master節點,若是master節點爲null,則30s後再次執行innerExecute方法,或者集羣狀態更新時調用innerExecute方法,從1開始,只能重新執行一次,若重新執行依然沒有master節點,返回失敗響應。若是master節點不爲null執行4
4.轉發請求到master節點,並new TransportResponseHandler,在接收到master的響應是,再轉發響應到客戶端。master節點接收到請求後,從頭開始執行,不出意外的話,會執行到步驟2,調用masterOperation方法。

masterOperation方法,將request中的信息生成CreateIndexClusterStateUpdateRequest對象,然後調用了MetaDataCreateIndexService中的createIndex方法。主要進行以下操作

1.構建索引的setting
2.根據索引名稱獲取鎖,若是獲取成功則調用重載的createIndex方法,否則new 一個Runnable,其中使用tryAcquire(long timeout, TimeUnit unit)方法來獲取鎖,等待時間是請求的timeout時間。
若是在等待時間內獲取到鎖,則調用重載的createIndex方法。

在createIndex方法中將索引創建任務封裝成一個集羣狀態更新任務,提交到一個優先級線程池中取執行,索引的創建流程如下:
1)驗證索引名稱,判斷路由表中是否含有此索引,元數據中是否含有次索引,索引名稱是否合法等
2)根據索引名稱查找templates,並排序,解析request中的mappings,和customs(暫時不知道這個是什麼)
3)合併mapping,mapping 包含以下幾部分

1.request的mapping
2.匹配到的templates
3.home/config/mappings/indexName 下的mapping
4.home/config/mappings/_default下的mapping
5._default_ 的mapping type (也就是索引類型 也叫mappings類型)
優先級從高到底

3)合併custom,將template中的custom與request中的合併
4)合併索引的setting,主要包含以下幾部分

1.request中的indexSetting
2.templates中的setting
3.ES setting中的配置的默認值
4.默認值
優先級從高到低

5)使用索引名稱和索引配置來創建索引,調用InternalIndicesService中的createIndex方法,初始化索引相關的各個模塊

public synchronized IndexService createIndex(String sIndexName, Settings settings, String localNodeId) throws ElasticsearchException {
    if (!lifecycle.started()) {  //判斷生命週期 是否是started
        throw new ElasticsearchIllegalStateException("Can't create an index [" + sIndexName + "], node is closed");
    }
    Index index = new Index(sIndexName);// new index
    if (indicesInjectors.containsKey(index.name())) {
        throw new IndexAlreadyExistsException(index);
    }

    indicesLifecycle.beforeIndexCreated(index);// 調用生命週期對象中的beforeIndexCreated方法

    logger.debug("creating Index [{}], shards [{}]/[{}]", sIndexName, settings.get(SETTING_NUMBER_OF_SHARDS), settings.get(SETTING_NUMBER_OF_REPLICAS));

    Settings indexSettings = settingsBuilder()
            .put(this.settings)
            .put(settings)
            .classLoader(settings.getClassLoader())
            .build();

    ModulesBuilder modules = new ModulesBuilder();
    modules.add(new IndexNameModule(index));//索引名稱
    modules.add(new LocalNodeIdModule(localNodeId)); //就用來記錄localNodeId
    modules.add(new IndexSettingsModule(index, indexSettings)); //索引setting   inject IndexSettingsService  和setting
    modules.add(new IndexPluginsModule(indexSettings, pluginsService));  // 索引plugin 注入
    modules.add(new IndexStoreModule(indexSettings));  // 根據配置 注入不同的store對象
    /*
     默認engine
     public static final Class<? extends Module> DEFAULT_INDEX_ENGINE = InternalIndexEngineModule.class;
     public static final Class<? extends Module> DEFAULT_ENGINE = InternalEngineModule.class;
     */
    modules.add(new IndexEngineModule(indexSettings));
    /*
    解析 模塊  各種關鍵字處理   pattern_replace
    CharFiltersBindings   字符處理
    TokenFiltersBindings    詞元處理
    TokenizersBindings  分詞處理
    AnalyzersBindings   analyzer 分析器   封裝了以上處理單元
    https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-analyzers.html
     */
    modules.add(new AnalysisModule(indexSettings, indicesAnalysisService));
    //我們要修改打分機制,就需要自定義similarity   BM25Similarity
    modules.add(new SimilarityModule(indexSettings));
    /*
    索引相關 cache
     */
    modules.add(new IndexCacheModule(indexSettings));
    // fieldData  doc_value  聚合功能
    modules.add(new IndexFieldDataModule(indexSettings));
    // 編解碼模塊   PostingsFormatProvider   DocValuesFormatProvider
    //PostingsFormat covers the inverted index, including all fields, terms, documents,
    // positions, payloads and offsets. Queries use this API to find their matching documents.
    modules.add(new CodecModule(indexSettings));

    //mapping
    modules.add(new MapperServiceModule());
    //查詢解析
    modules.add(new IndexQueryParserModule(indexSettings));
    //別名
    modules.add(new IndexAliasesServiceModule());
    // gateway  full restart 索引恢復
    modules.add(new IndexGatewayModule(indexSettings, injector.getInstance(Gateway.class)));
    // 索引服務  包含了上邊的各個服務模塊,直接使用indexService
    modules.add(new IndexModule(indexSettings));

    Injector indexInjector;
    try {
        // 創建一個子注入器
        indexInjector = modules.createChildInjector(injector);
    } catch (CreationException e) {
        throw new IndexCreationException(index, Injectors.getFirstErrorFailure(e));
    } catch (Throwable e) {
        throw new IndexCreationException(index, e);
    }
    // 映射關係
    indicesInjectors.put(index.name(), indexInjector);

    //// 索引服務  包含了上邊的各個服務模塊,直接使用indexService
    IndexService indexService = indexInjector.getInstance(IndexService.class);

    // 生命週期
    indicesLifecycle.afterIndexCreated(indexService);

    indices = newMapBuilder(indices).put(index.name(), indexService).immutableMap();

    return indexService;
}

6)將mappings信息添加到MapperService中,然後根據MapperService生成MappingMetaData元數據。每個type對應一個MappingMetaData。
7)根據索引名稱,索引setting,mapping元數據MappingMetaData,custom,和請求中的索引狀態來構建一個索引元數據IndexMetaData。將索引元數據添加到元數據中MetaData
8)若是request中有索引對應的block信息,則添加到ClusterBlocks中,若是request的state等於close(索引關閉),則添加此block到ClusterBlocks中。block用來定義索引的訪問權限

write read metadata 三種類型阻塞
                        block
index read-only  / write  metadata
index read  / read
index write /write
index metadata /metadata
index close /read write

9)更新metaData 和 block 到ClusterState 中
10)若是request的state等於Open,則構建routingTable,進行分片分配,這個過程由ShardsAllocator接口的實現類和AllocationDecider抽象類的子類來完成。
11)對比創建以後的集羣狀態和前的狀態,有變化則集羣狀態的version,routingTable的version與metadata的version都會加1,構建一個新的ClusterState,發佈集羣狀態到所有節點,將集羣狀態更新事件作爲參數調用各個集羣狀態更新的listener。等到收到所有節點的ack信息,則釋放鎖,返回Response

1.源碼閱讀筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章