elasticsearch索引建立過程

開篇

?懷著佛系心態(tài)寫的文章川慌,因為發(fā)現(xiàn)心急依然看不懂代碼,所以只能安慰自己佛系一點塔淤,這篇文章希望能夠把elasticsearch的index的創(chuàng)建過程講清楚(包括index但不包括doc的添加過程)驹吮,希望能夠有幫助依疼。


ES的Meta的組成

?Meta是用來描述數(shù)據(jù)的數(shù)據(jù)。在ES中材鹦,Index的mapping結(jié)構(gòu)逝淹、配置、持久化狀態(tài)等就屬于meta數(shù)據(jù)桶唐,集群的一些配置信息也屬于meta栅葡。ES的Meta信息可以簡單的理解為包括ClusterState、MetaData尤泽、IndexMetaData欣簇。

ClusterState
?集群中的每個節(jié)點都會在內(nèi)存中維護(hù)一個當(dāng)前的ClusterState,表示當(dāng)前集群的各種狀態(tài)坯约。ClusterState中包含一個MetaData的結(jié)構(gòu)熊咽,MetaData中存儲的內(nèi)容更符合meta的特征,而且需要持久化的信息都在MetaData中闹丐,此外的一些變量可以認(rèn)為是一些臨時狀態(tài)横殴,是集群運行中動態(tài)構(gòu)建出來的。

public class ClusterState implements ToXContentFragment, Diffable<ClusterState> {
    public static final String UNKNOWN_UUID = "_na_";
    public static final long UNKNOWN_VERSION = -1;
    private final long version;
    private final String stateUUID;
    private final RoutingTable routingTable;
    private final DiscoveryNodes nodes;
    private final MetaData metaData;
    private final ClusterBlocks blocks;
    private final ImmutableOpenMap<String, Custom> customs;
    private final ClusterName clusterName;
    private final boolean wasReadFromDiff;
}



MetaData
?MetaData主要是集群的一些配置卿拴,集群所有Index的Meta衫仑,所有Template的Meta梨与,所有custom的Meta信息。

public class MetaData implements Iterable<IndexMetaData>, Diffable<MetaData>, ToXContentFragment {
    private final String clusterUUID;
    private final long version;
    private final Settings transientSettings;
    private final Settings persistentSettings;
    private final Settings settings;
    private final ImmutableOpenMap<String, IndexMetaData> indices;
    private final ImmutableOpenMap<String, IndexTemplateMetaData> templates;
    private final ImmutableOpenMap<String, Custom> customs;
    private final transient int totalNumberOfShards; 
    private final int numberOfShards;
    private final String[] allIndices;
    private final String[] allOpenIndices;
    private final String[] allClosedIndices;
    private final SortedMap<String, AliasOrIndex> aliasAndIndexLookup;



IndexMetaData
?IndexMetaData指具體某個Index的Meta惑畴,比如Index的shard蛋欣、replica航徙、mappings等

public class IndexMetaData implements Diffable<IndexMetaData>, ToXContentFragment {
    private final int routingNumShards;
    private final int routingFactor;
    private final int routingPartitionSize;
    private final int numberOfShards;
    private final int numberOfReplicas;
    private final Index index;
    private final long version;
    private final long[] primaryTerms;
    private final State state;
    private final ImmutableOpenMap<String, AliasMetaData> aliases;
    private final Settings settings;
    private final ImmutableOpenMap<String, MappingMetaData> mappings;
    private final ImmutableOpenMap<String, Custom> customs;
    private final ImmutableOpenIntMap<Set<String>> inSyncAllocationIds;
    private final transient int totalNumberOfShards;
    private final DiscoveryNodeFilters requireFilters;
    private final DiscoveryNodeFilters includeFilters;
    private final DiscoveryNodeFilters excludeFilters;
    private final DiscoveryNodeFilters initialRecoveryFilters;
    private final Version indexCreatedVersion;
    private final Version indexUpgradedVersion;
    private final ActiveShardCount waitForActiveShards;
}


索引建立過程

階段一:校驗參數(shù)階段

  • 校驗當(dāng)前ClusterState的currentState是否存在該索引如贷,routingTable包含該index、metaData包含該index到踏、alias別名等杠袱。

階段二:配置合并階段

  • 合并template和request傳入的mapping、customs 數(shù)據(jù)窝稿,優(yōu)先級上request配置優(yōu)先于template楣富。

  • 合并template和request的setting,優(yōu)先級上request配置優(yōu)先于template伴榔。

階段三:構(gòu)建IndexSettings階段

  • 構(gòu)建Settings.Builder indexSettingsBuilder對象纹蝴,合并templates、request的數(shù)據(jù)踪少,輔以默認(rèn)配置值塘安。

階段四:構(gòu)建IndexMetaData階段

  • 構(gòu)建IndexMetaData.Builder的tmpImdBuilder對象并綁定indexSettingsBuilder生成的actualIndexSettings。

  • 通過tmpImdBuilder.build()構(gòu)建構(gòu)建IndexMetaData tmpImd對象援奢。

階段五:構(gòu)建IndexService階段

  • 臨時構(gòu)建indexService對象兼犯,IndexService indexService = indicesService.createIndex(tmpImd, Collections.emptyList())。

階段六:獲取Index和Map階段

  • 獲取新創(chuàng)建的index和mapping集漾,Index createdIndex = indexService.index()和MapperService mapperService = indexService.mapperService()切黔。

階段七:更新mapping到mapperService階段

  • mapperService.merge()合并request和template合并后生成的最新的mappings。

  • 生成Map<String, MappingMetaData> mappingsMetaData具篇,key是創(chuàng)建mapping的指定的type纬霞。

階段八:構(gòu)建IndexMetaData階段

  • 構(gòu)建IndexMetaData.Builder indexMetaDataBuilder的indexMetaDataBuilder對象并綁定actualIndexSettings和routingNumShards。

  • indexMetaDataBuilder通過primaryTerm設(shè)置primaryTerm驱显、putMapping設(shè)置mappingMd险领,putAlias設(shè)置aliasMetaData(template和request請求),putCustom設(shè)置customIndexMetaData秒紧,indexMetaDataBuilder.state設(shè)置state绢陌。

  • 創(chuàng)建indexMetaData對象,通過indexMetaData = indexMetaDataBuilder.build()實現(xiàn)熔恢。

階段九:更新IndexMetaData階段

  • 將IndexMetaData對象更新到MetaData對象中并返回最新的MetaData脐湾, MetaData newMetaData = MetaData.builder(currentState.metaData()).put(indexMetaData, false).build()。

階段十:更新MetaData階段

  • 更新最新的MetaData對象newMetaData對象到ClusterState叙淌,ClusterState.builder(currentState).blocks(blocks).metaData(newMetaData).build()秤掌。
public class MetaDataCreateIndexService extends AbstractComponent {

    static class IndexCreationTask extends AckedClusterStateUpdateTask<ClusterStateUpdateResponse> {

        @Override
        /**
         * es當(dāng)前集群的狀態(tài)ClusterState
         */
        public ClusterState execute(ClusterState currentState) throws Exception {
            Index createdIndex = null;
            String removalExtraInfo = null;
            IndexRemovalReason removalReason = IndexRemovalReason.FAILURE;
            try {
                // 以下是校驗ClusterState中是否存在index索引愁铺。
                // 校驗當(dāng)前currentState是否存在該索引,routingTable包含該index闻鉴、metaData包含該index茵乱、alias別名等。
                validator.validate(request, currentState);

                // 校驗是否已存在別名
                for (Alias alias : request.aliases()) {
                    aliasValidator.validateAlias(alias, request.index(), currentState.metaData());
                }

                // 以下過程是合并request和template的配置信息
                // 從當(dāng)前的ClusterState查詢是否存在別名
                List<IndexTemplateMetaData> templates = findTemplates(request, currentState);

                // 用來保存請求帶過來的mapping
                Map<String, Custom> customs = new HashMap<>();
                Map<String, Map<String, Object>> mappings = new HashMap<>();
                Map<String, AliasMetaData> templatesAliases = new HashMap<>();
                List<String> templateNames = new ArrayList<>();

                // 保存request的mapping到臨時變量mappings
                for (Map.Entry<String, String> entry : request.mappings().entrySet()) {
                    mappings.put(entry.getKey(), MapperService.parseMapping(xContentRegistry, entry.getValue()));
                }

                // 保存request的customs信息到臨時變量customs當(dāng)中
                for (Map.Entry<String, Custom> entry : request.customs().entrySet()) {
                    customs.put(entry.getKey(), entry.getValue());
                }

                final Index recoverFromIndex = request.recoverFrom();

                // todo 這里不知道是什么判斷孟岛,大概是不需要從某個索引進(jìn)行恢復(fù)
                if (recoverFromIndex == null) {
                    // 合并template的mapping瓶竭、customs、alias到request的參數(shù)當(dāng)中
                    for (IndexTemplateMetaData template : templates) {
                        templateNames.add(template.getName());

                        // 合并request和template的mapping變量
                        for (ObjectObjectCursor<String, CompressedXContent> cursor : template.mappings()) {
                            String mappingString = cursor.value.string();
                            // 如果request包含該命名的mapping渠羞,就進(jìn)行合并,mapping以request傳入為主斤贰,合并命中的template
                            if (mappings.containsKey(cursor.key)) {
                                XContentHelper.mergeDefaults(mappings.get(cursor.key),
                                    MapperService.parseMapping(xContentRegistry, mappingString));
                            } else {
                                // 如果request不包含該命名的mapping,就直接新增即可
                                mappings.put(cursor.key,
                                    MapperService.parseMapping(xContentRegistry, mappingString));
                            }
                        }
                        // handle custom
                        for (ObjectObjectCursor<String, Custom> cursor : template.customs()) {
                            String type = cursor.key;
                            IndexMetaData.Custom custom = cursor.value;
                            IndexMetaData.Custom existing = customs.get(type);
                            if (existing == null) {
                                customs.put(type, custom);
                            } else {
                                IndexMetaData.Custom merged = existing.mergeWith(custom);
                                customs.put(type, merged);
                            }
                        }

                        // 以request帶的alias作為為主次询,合并template的alias
                        for (ObjectObjectCursor<String, AliasMetaData> cursor : template.aliases()) {
                            AliasMetaData aliasMetaData = cursor.value;

                            if (request.aliases().contains(new Alias(aliasMetaData.alias()))) {
                                continue;
                            }

                            if (templatesAliases.containsKey(cursor.key)) {
                                continue;
                            }

                            if (aliasMetaData.alias().contains("{index}")) {
                                String templatedAlias = aliasMetaData.alias().replace("{index}", request.index());
                                aliasMetaData = AliasMetaData.newAliasMetaData(aliasMetaData, templatedAlias);
                            }

                            aliasValidator.validateAliasMetaData(aliasMetaData, request.index(), currentState.metaData());
                            templatesAliases.put(aliasMetaData.alias(), aliasMetaData);
                        }
                    }
                }

                // 以下是創(chuàng)建setting過程荧恍,用于創(chuàng)建索引時候使用。
                // 合并templates的setting配置
                Settings.Builder indexSettingsBuilder = Settings.builder();
                if (recoverFromIndex == null) {
                    // apply templates, here, in reverse order, since first ones are better matching
                    for (int i = templates.size() - 1; i >= 0; i--) {
                        indexSettingsBuilder.put(templates.get(i).settings());
                    }
                }

                // 合并request的setting并覆蓋templates的setting
                indexSettingsBuilder.put(request.settings());
                if (indexSettingsBuilder.get(SETTING_NUMBER_OF_SHARDS) == null) {
                    indexSettingsBuilder.put(SETTING_NUMBER_OF_SHARDS, settings.getAsInt(SETTING_NUMBER_OF_SHARDS, 5));
                }
                if (indexSettingsBuilder.get(SETTING_NUMBER_OF_REPLICAS) == null) {
                    indexSettingsBuilder.put(SETTING_NUMBER_OF_REPLICAS, settings.getAsInt(SETTING_NUMBER_OF_REPLICAS, 1));
                }
                if (settings.get(SETTING_AUTO_EXPAND_REPLICAS) != null && 
                    indexSettingsBuilder.get(SETTING_AUTO_EXPAND_REPLICAS) == null) {
                    indexSettingsBuilder.put(SETTING_AUTO_EXPAND_REPLICAS, settings.get(SETTING_AUTO_EXPAND_REPLICAS));
                }

                if (indexSettingsBuilder.get(SETTING_VERSION_CREATED) == null) {
                    DiscoveryNodes nodes = currentState.nodes();
                    final Version createdVersion = Version.min(Version.CURRENT, nodes.getSmallestNonClientNodeVersion());
                    indexSettingsBuilder.put(SETTING_VERSION_CREATED, createdVersion);
                }

                if (indexSettingsBuilder.get(SETTING_CREATION_DATE) == null) {
                    indexSettingsBuilder.put(SETTING_CREATION_DATE, new DateTime(DateTimeZone.UTC).getMillis());
                }
                indexSettingsBuilder.put(IndexMetaData.SETTING_INDEX_PROVIDED_NAME, request.getProvidedName());
                indexSettingsBuilder.put(SETTING_INDEX_UUID, UUIDs.randomBase64UUID());

                // 以下是創(chuàng)建IndexMetaData的builder的過程
                // 組建IndexMetaData的builder
                final IndexMetaData.Builder tmpImdBuilder = IndexMetaData.builder(request.index());

                final int routingNumShards;

                // routingNumShards的獲取邏輯沒怎么看懂
                if (recoverFromIndex == null) {
                    Settings idxSettings = indexSettingsBuilder.build();
                    routingNumShards = IndexMetaData.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.get(idxSettings);
                } else {
                    assert IndexMetaData.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.exists(indexSettingsBuilder.build()) == false
                        : "index.number_of_routing_shards should be present on the target index on resize";
                    final IndexMetaData sourceMetaData = currentState.metaData().getIndexSafe(recoverFromIndex);
                    routingNumShards = sourceMetaData.getRoutingNumShards();
                }
                // 移除routing_shards配置
                indexSettingsBuilder.remove(IndexMetaData.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey());
                tmpImdBuilder.setRoutingNumShards(routingNumShards);

                if (recoverFromIndex != null) {
                    assert request.resizeType() != null;
                    prepareResizeIndexSettings(
                        currentState, mappings.keySet(), 
                        indexSettingsBuilder, recoverFromIndex, request.index(), request.resizeType());
                }

                // 實際的IndexSetting
                final Settings actualIndexSettings = indexSettingsBuilder.build();

                // IndexMetaData的builder添加實際的索引的設(shè)置
                tmpImdBuilder.settings(actualIndexSettings);

                if (recoverFromIndex != null) {

                    final IndexMetaData sourceMetaData = currentState.metaData().getIndexSafe(recoverFromIndex);
                    final long primaryTerm =
                        IntStream
                            .range(0, sourceMetaData.getNumberOfShards())
                            .mapToLong(sourceMetaData::primaryTerm)
                            .max()
                            .getAsLong();
                    for (int shardId = 0; shardId < tmpImdBuilder.numberOfShards(); shardId++) {
                        tmpImdBuilder.primaryTerm(shardId, primaryTerm);
                    }
                }

                // 創(chuàng)建實際的IndexMetaData對象
                final IndexMetaData tmpImd = tmpImdBuilder.build();
                ActiveShardCount waitForActiveShards = request.waitForActiveShards();
                if (waitForActiveShards == ActiveShardCount.DEFAULT) {
                    waitForActiveShards = tmpImd.getWaitForActiveShards();
                }
                if (waitForActiveShards.validate(tmpImd.getNumberOfReplicas()) == false) {
                    throw new IllegalArgumentException("invalid wait_for_active_shards[" + request.waitForActiveShards() +
                        "]: cannot be greater than number of shard copies [" +
                        (tmpImd.getNumberOfReplicas() + 1) + "]");
                }

                // 創(chuàng)建索引服務(wù)IndexService
                final IndexService indexService = indicesService.createIndex(tmpImd, Collections.emptyList());
                createdIndex = indexService.index();

                // 獲取創(chuàng)建IndexService的mapperService
                MapperService mapperService = indexService.mapperService();
                try {
                    // 合并request和template合并后生成的最新的mappings
                    mapperService.merge(mappings, MergeReason.MAPPING_UPDATE, request.updateAllTypes());
                } catch (Exception e) {
                    removalExtraInfo = "failed on parsing default mapping/mappings on index creation";
                    throw e;
                }

                if (request.recoverFrom() == null) {
                    indexService.getIndexSortSupplier().get();
                }

                final QueryShardContext queryShardContext = indexService.newQueryShardContext(0, null, () -> 0L, null);

                for (Alias alias : request.aliases()) {
                    if (Strings.hasLength(alias.filter())) {
                        aliasValidator.validateAliasFilter(alias.name(), alias.filter(), queryShardContext, xContentRegistry);
                    }
                }
                for (AliasMetaData aliasMetaData : templatesAliases.values()) {
                    if (aliasMetaData.filter() != null) {
                        aliasValidator.validateAliasFilter(aliasMetaData.alias(), aliasMetaData.filter().uncompressed(),
                            queryShardContext, xContentRegistry);
                    }
                }

                // 新建mappingsMetaData對象
                Map<String, MappingMetaData> mappingsMetaData = new HashMap<>();
                for (DocumentMapper mapper : mapperService.docMappers(true)) {
                    MappingMetaData mappingMd = new MappingMetaData(mapper);
                    mappingsMetaData.put(mapper.type(), mappingMd);
                }

                // 以下過程是創(chuàng)建IndexMetaData的過程
                // 創(chuàng)建indexMetaDataBuilder對象屯吊,執(zhí)行真正的創(chuàng)建
                final IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder(request.index())
                    .settings(actualIndexSettings)
                    .setRoutingNumShards(routingNumShards);

                for (int shardId = 0; shardId < tmpImd.getNumberOfShards(); shardId++) {
                    indexMetaDataBuilder.primaryTerm(shardId, tmpImd.primaryTerm(shardId));
                }

                for (MappingMetaData mappingMd : mappingsMetaData.values()) {
                    indexMetaDataBuilder.putMapping(mappingMd);
                }

                for (AliasMetaData aliasMetaData : templatesAliases.values()) {
                    indexMetaDataBuilder.putAlias(aliasMetaData);
                }
                for (Alias alias : request.aliases()) {
                    AliasMetaData aliasMetaData = AliasMetaData.builder(alias.name()).filter(alias.filter())
                        .indexRouting(alias.indexRouting()).searchRouting(alias.searchRouting()).build();
                    indexMetaDataBuilder.putAlias(aliasMetaData);
                }

                for (Map.Entry<String, Custom> customEntry : customs.entrySet()) {
                    indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue());
                }

                indexMetaDataBuilder.state(request.state());

                // 創(chuàng)建indexMetaData對象
                final IndexMetaData indexMetaData;
                try {
                    indexMetaData = indexMetaDataBuilder.build();
                } catch (Exception e) {
                    removalExtraInfo = "failed to build index metadata";
                    throw e;
                }

                indexService.getIndexEventListener().beforeIndexAddedToCluster(indexMetaData.getIndex(),
                    indexMetaData.getSettings());

                // 創(chuàng)建新的MetaData
                MetaData newMetaData = MetaData.builder(currentState.metaData())
                    .put(indexMetaData, false)
                    .build();

                ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(currentState.blocks());
                if (!request.blocks().isEmpty()) {
                    for (ClusterBlock block : request.blocks()) {
                        blocks.addIndexBlock(request.index(), block);
                    }
                }
                blocks.updateBlocks(indexMetaData);

                // 阻塞blocks并更新Meta元數(shù)據(jù)
                ClusterState updatedState = ClusterState.builder(currentState).blocks(blocks).metaData(newMetaData).build();

                if (request.state() == State.OPEN) {
                    RoutingTable.Builder routingTableBuilder = RoutingTable.builder(updatedState.routingTable())
                        .addAsNew(updatedState.metaData().index(request.index()));
                    updatedState = allocationService.reroute(
                        ClusterState.builder(updatedState).routingTable(routingTableBuilder.build()).build(),
                        "index [" + request.index() + "] created");
                }
                removalExtraInfo = "cleaning up after validating index on master";
                removalReason = IndexRemovalReason.NO_LONGER_ASSIGNED;
                return updatedState;
            } finally {
                if (createdIndex != null) {
                    indicesService.removeIndex(createdIndex, removalReason, removalExtraInfo);
                }
            }
        }
    }
}


創(chuàng)建IndexService過程

這部分邏輯暫時沒有深入研究送巡,這里只放在這里用于引導(dǎo)提醒一下,具體不做深入研究盒卸。

public class IndicesService extends AbstractLifecycleComponent
    implements IndicesClusterStateService.AllocatedIndices<IndexShard, IndexService>, IndexService.ShardStoreDeleter {

    public synchronized IndexService createIndex(
            final IndexMetaData indexMetaData, final List<IndexEventListener> builtInListeners) throws IOException {
        ensureChangesAllowed();
        if (indexMetaData.getIndexUUID().equals(IndexMetaData.INDEX_UUID_NA_VALUE)) {
            throw new IllegalArgumentException("index must have a real UUID found value: [" + indexMetaData.getIndexUUID() + "]");
        }
        final Index index = indexMetaData.getIndex();
        if (hasIndex(index)) {
            throw new ResourceAlreadyExistsException(index);
        }
        List<IndexEventListener> finalListeners = new ArrayList<>(builtInListeners);
        final IndexEventListener onStoreClose = new IndexEventListener() {
            @Override
            public void onStoreClosed(ShardId shardId) {
                indicesQueryCache.onClose(shardId);
            }
        };
        finalListeners.add(onStoreClose);
        finalListeners.add(oldShardsStats);
        final IndexService indexService =
                createIndexService(
                        "create index",
                        indexMetaData,
                        indicesQueryCache,
                        indicesFieldDataCache,
                        finalListeners,
                        indexingMemoryController);
        boolean success = false;
        try {
            indexService.getIndexEventListener().afterIndexCreated(indexService);
            indices = newMapBuilder(indices).put(index.getUUID(), indexService).immutableMap();
            success = true;
            return indexService;
        } finally {
            if (success == false) {
                indexService.close("plugins_failed", true);
            }
        }
    }


    private synchronized IndexService createIndexService(final String reason,
                                                         IndexMetaData indexMetaData,
                                                         IndicesQueryCache indicesQueryCache,
                                                         IndicesFieldDataCache indicesFieldDataCache,
                                                         List<IndexEventListener> builtInListeners,
                                                         IndexingOperationListener... indexingOperationListeners) throws IOException {
        final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexScopeSetting);
        logger.debug("creating Index [{}], shards [{}]/[{}] - reason [{}]",
            indexMetaData.getIndex(),
            idxSettings.getNumberOfShards(),
            idxSettings.getNumberOfReplicas(),
            reason);

        final IndexModule indexModule = new IndexModule(idxSettings, analysisRegistry);
        for (IndexingOperationListener operationListener : indexingOperationListeners) {
            indexModule.addIndexOperationListener(operationListener);
        }
        pluginsService.onIndexModule(indexModule);
        for (IndexEventListener listener : builtInListeners) {
            indexModule.addIndexEventListener(listener);
        }
        return indexModule.newIndexService(
                nodeEnv,
                xContentRegistry,
                this,
                circuitBreakerService,
                bigArrays,
                threadPool,
                scriptService,
                client,
                indicesQueryCache,
                mapperRegistry,
                indicesFieldDataCache,
                namedWriteableRegistry
        );
    }
}


參考文章

Elasticsearch分布式一致性原理剖析(二)-Meta篇
elasticsearch index 之 create index(二)


招聘信息

【招賢納士】

歡迎熱愛技術(shù)骗爆、熱愛生活的你和我成為同事,和貝貝共同成長世落。

貝貝集團誠招算法淮腾、大數(shù)據(jù)、BI屉佳、Java谷朝、PHP、android武花、iOS圆凰、測試、運維体箕、DBA等人才专钉,有意可投遞zhi.wang@beibei.com

貝貝集團創(chuàng)建于2011年累铅,旗下?lián)碛胸愗惥W(wǎng)跃须、貝店、貝貸等平臺娃兽,致力于成為全球領(lǐng)先的家庭消費平臺菇民。

貝貝創(chuàng)始團隊來自阿里巴巴,先后獲得IDG資本、高榕資本第练、今日資本阔馋、新天域資本、北極光等數(shù)億美金的風(fēng)險投資娇掏。

公司地址:杭州市江干區(qū)普盛巷9號東谷創(chuàng)業(yè)園(上下班有多趟班車)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呕寝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子婴梧,更是在濱河造成了極大的恐慌下梢,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件志秃,死亡現(xiàn)場離奇詭異怔球,居然都是意外死亡嚼酝,警方通過查閱死者的電腦和手機浮还,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闽巩,“玉大人钧舌,你說我怎么就攤上這事∠芽纾” “怎么了洼冻?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長隅很。 經(jīng)常有香客問我撞牢,道長,這世上最難降的妖魔是什么叔营? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任屋彪,我火速辦了婚禮,結(jié)果婚禮上绒尊,老公的妹妹穿的比我還像新娘畜挥。我一直安慰自己,他們只是感情好婴谱,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布蟹但。 她就那樣靜靜地躺著,像睡著了一般谭羔。 火紅的嫁衣襯著肌膚如雪华糖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天瘟裸,我揣著相機與錄音客叉,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛十办,可吹牛的內(nèi)容都是我干的秀撇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼向族,長吁一口氣:“原來是場噩夢啊……” “哼呵燕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起件相,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤再扭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夜矗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泛范,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年紊撕,在試婚紗的時候發(fā)現(xiàn)自己被綠了罢荡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡对扶,死狀恐怖区赵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浪南,我是刑警寧澤笼才,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站络凿,受9級特大地震影響骡送,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜絮记,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一摔踱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧到千,春花似錦昌渤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至了赵,卻和暖如春潜支,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柿汛。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工冗酿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留埠对,地道東北人。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓裁替,卻偏偏與公主長得像项玛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子弱判,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架襟沮,建立于...
    Hsinwong閱讀 22,394評論 1 92
  • 原文鏈接:https://docs.spring.io/spring-boot/docs/1.4.x/refere...
    pseudo_niaonao閱讀 4,692評論 0 9
  • 1.印象深刻的三個部分: ①桂昌浩串講知識, ②老師講解內(nèi)容并且進(jìn)行意志薄弱的測試昌腰。 ③播放兩段視頻开伏。 2.為何印...
    寂寞先森彡閱讀 181評論 0 1
  • 上小學(xué)五年,他從未當(dāng)過“三好生”遭商,也從未想過當(dāng)“三好生”固灵,盡管他成績不錯,表現(xiàn)也很好劫流。因為他是個農(nóng)家孩子巫玻,只...
    汪汪_00a1閱讀 193評論 0 0
  • 客戶端模塊化解耦實踐 - Router 背景 隨著業(yè)務(wù)進(jìn)入快速發(fā)展期,業(yè)務(wù)線拓展迅速困介,項目結(jié)構(gòu)變得龐大復(fù)雜大审,導(dǎo)致迭...
    sandking閱讀 1,335評論 4 15