当前位置:首页 > 科技  > 软件

ElasticSearch集群灾难:别放弃,也许能再抢救一下

来源: 责编: 时间:2024-04-07 17:02:07 84观看
导读1 前言Elasticsearch作为一个分布式搜索引擎,自身是高可用的;但也架不住一些特殊情况的发生,如:集群超过半数的master节点丢失,ES的节点无法形成一个集群,进而导致集群不可用;索引shard的文件损坏,分片无法被正常恢复,进而导致
1 前言

j8k28资讯网——每日最新资讯28at.com

Elasticsearch作为一个分布式搜索引擎,自身是高可用的;但也架不住一些特殊情况的发生,如:j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

集群超过半数的master节点丢失,ES的节点无法形成一个集群,进而导致集群不可用;j8k28资讯网——每日最新资讯28at.com

索引shard的文件损坏,分片无法被正常恢复,进而导致索引无法正常提供服务j8k28资讯网——每日最新资讯28at.com

本地盘节点,多数据节点故障,旧节点无法再次加入集群,数据丢失j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

针对上述的情况,今天来聊一聊相关的解决方案。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

2 基础知识

j8k28资讯网——每日最新资讯28at.com

2.1 集群经典架构

j8k28资讯网——每日最新资讯28at.com

在聊解决方案之前,首先来看一看ES集群层面的基本知识,es的集群组成通常如图1-1所示j8k28资讯网——每日最新资讯28at.com

图 1-1 es常用集群架构图 1-1 es常用集群架构j8k28资讯网——每日最新资讯28at.com

如图1-1所示,为生产环境es集群的经典架构,主要由专有主节点、专有协调节点和数据节点组成:j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

  • 专有主节点(Master-eligible node): 具有master角色的节点,这使其有资格被选为主节点,只存储集群元信息包含cluster、index、shard级别的元数据;该种角色节点被选举为master之后,将作为整个ES集群的大脑,负责维护集群层面的元信息,创建删除索引等工作。该种节点的个数必须为奇数,通常我们固定为3个,如果该类节点丢失半数,es集群将无法维持es节点形成一个集群。
  • 专有协调节点(网关节点): 该种节点不具有任何角色,仅仅用来处理es请求;比如(1)将写请求的数据归类转发到数据所属的节点(2)查询请求的二次聚合计算。通常我们也会给该类节点保留ingest角色 ,ingest的主要作用是对数据进行预处理;比如:字段重命名、给数据文档打上指纹和清洗数据等功能主要通过pipeline能力进行处理
  • 数据节点(Data node): 存储数据和集群元信息,执行与数据相关的操作,如CRUD、搜索和聚合。在数据节点上打上不同的属性,可以使其成为hot、warm、cold数据节点,在es7.9版本之后配置略有不同,但是原理基本不变。

j8k28资讯网——每日最新资讯28at.com

如果没有显示设置节点角色,es的每个节点都会含有以上三种角色。除此之后还有Remote-eligible node 、ml-node和Transform nodes等角色需要显示的配置,节点才会有该角色。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

2.2 集群元信息

j8k28资讯网——每日最新资讯28at.com

集群完全启动主要包含选举主节点、元信息、主分片、数据恢复等重要阶段;如图2-1所示[1]。j8k28资讯网——每日最新资讯28at.com

图 2-1 es集群启动流程图 2-1 es集群启动流程j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

主节点选举的过程,不是本文的重点,而是集群元信息的选举。被选举出的master和集群元信息新旧程度没有关系;master节点被选举出来之后,它所要完成的第一个任务,即是选举集群元信息。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

(1)Master选举成功之后,判断其持有的集群状态中是否存在STATE_NOT_RECOVERED_BLOCK,如果不存在,则说明元数据已经恢复,跳过gateway恢复过程,否则等待。org.elasticsearch.gateway.GatewayService#clusterChangedj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

//跳过元数据恢复if (state.blocks().hasGlobalBlock(STATE_NOT_RECOVERED_BLOCK) == false) {            // already recovered            return; } //此处省略部分代码。 //进入gateway恢复过程   performStateRecovery(enforceRecoverAfterTime, reason);

j8k28资讯网——每日最新资讯28at.com

(2)Master从各个节点主动获取元数据信息。org.elasticsearch.gateway.Gateway#performStateRecoveryj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

# 获取元信息核心代码 final String[] nodesIds = clusterService.state().nodes().getMasterNodes().keys().toArray(String.class);        logger.trace("performing state recovery from {}", Arrays.toString(nodesIds));        final TransportNodesListGatewayMetaState.NodesGatewayMetaState nodesState = listGatewayMetaState.list(nodesIds, null).actionGet();

j8k28资讯网——每日最新资讯28at.com

(3)从获取的元数据信息中选择版本号最大的作为最新元数据;元信息包括集群级、索引级。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

## org.elasticsearch.gateway.Gateway#performStateRecovery    public void performStateRecovery(final GatewayStateRecoveredListener listener) throws GatewayException {# 省略若干行代码## 进入allocation阶段;## final Gateway.GatewayStateRecoveredListener recoveryListener = new GatewayRecoveryListener();## listener为 GatewayStateRecoveredListener    listener.onSuccess(builder.build());    }

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

(4)两者确定之后,调用allocation模块的reroute,对未分配 的分片执行分配,主分片分配过程中会异步获取各个shard级别元数据。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

#主要实现方法为如下方法   #org.elasticsearch.gateway.GatewayService.GatewayRecoveryListener#onSuccess## 主要工作是构建集群状态(ClusterState),其中的内容路由表 依赖allocation模块协助完成,调用 allocationService.reroute 进 入下一阶段:异步执行分片层元数据的恢复,以及分片分配。updateTask线程结束.

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

ES中存储的数据:(1)state元数据信息;(2)index Lucene生成的索引文件;(3)translog事务日志。元数据信息:j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

  • nodes/0/_state/*.st,集群层面元信息MetaData(clusterUUID 、 settings 、templates等);
  • nodes/0/indices/{index_uuid}/_state/*.st,索引层面元信息IndexMetaData( numberOfShards 、mappings等);
  • nodes/0/indices/{index_uuid}/0/_state/*.st,分片层面元信息ShardStateMetaData(version 、indexUUID、primary等)。

j8k28资讯网——每日最新资讯28at.com

上述信息被持久化到磁盘:持久化的state不包括某个分片存在于哪个节点这种内容路由信息,集群完全重启时,依靠gateway的recovery过程重建RoutingTable和RoutingNode。当读取某个文档时, 根据路由算法确定目的分片后,再从RoutingTable中查找分片位于哪个节点,然后将请求转发到目的节点[1]。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

⚠️ 注意:在es7.0.0之后es的元信息存储方式发生变化;es7.0.0之后元信息存储改使用lucene的方式存储,见pr50928 Move metadata storage to Lucene)j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

7.10.2 专有主节点,集群元数据j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

./|-- _state|   |-- _39h.cfe|   |-- _39h.cfs|   |-- _39h.si|   |-- node-0.st|   |-- segments_50d|   `-- write.lock`-- node.lock

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

6.8.13 专有主节点,集群元数据j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

./|-- _state|   |-- global-230.st|   `-- node-2.st|-- indices|   |-- -hiy4JnoRfqUJHTJoNUt4Q|   |   `-- _state|   |       `-- state-4.st|   `-- ylJKVlqISGOi8EkpxHE_2A|       `-- _state|           `-- state-6.st`-- node.lock

j8k28资讯网——每日最新资讯28at.com

3 灾难场景与处理方法

j8k28资讯网——每日最新资讯28at.com

3.1 master节点丢失

j8k28资讯网——每日最新资讯28at.com

⚠️ 注意本文所述的master节点个数,假设前提均为3个j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

场景1 master节点丢失过半

j8k28资讯网——每日最新资讯28at.com

master节点是控制整个集群;当该种节点角色丢失过半,由于集群中投票节点永远不可能达到quorum无法选主,将无法维持es节点形成一个集群;虽然集群无法形成一个集群,但所仍幸master-eligible节点存活,我们可以使用如下手段进行处理。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

es7.0.0版本之前

j8k28资讯网——每日最新资讯28at.com

  • 修改剩余节点的elasticsearch.yaml配置如下,修改quorum的个数,然后启动剩余的节点,形成一个新的集群;

j8k28资讯网——每日最新资讯28at.com

discovery.zen.minimum_master_nodes: 1discovery.zen.ping.unicast.hosts:- masters-0

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

  • 重建补充之前丢失的master-eligible节点,加入集群之后.3 将集群配置修改为旧的配置,再逐一重启下集群中的节点,先从master-eligible开始.

j8k28资讯网——每日最新资讯28at.com

es7.0.0(包含)版本之后.

j8k28资讯网——每日最新资讯28at.com

在es7.0.0版本之后,由于es修改集群的启动配置,新增配置discovery.seed_hosts 和cluster.initial_master_nodes;es集群第一次启动时称为bootstrap,该过程将配置文件中的cluster.initial_master_node作为初始的投票节点Voting configurations,投票节点具有选举master和commit cluster state的权利,超过半数以上同意即投票成功。如果在集群健康的场景下,我们需要下线超过半数的master-eligible;则必须首先使用投票配置排除API从投票配置中排除受影响的节点。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

POST _cluster/voting_config_exclusions?node_names={node_names}POST _cluster/voting_config_exclusions?node_ids={node_ids}DELETE _cluster/voting_config_exclusions

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

但是如果丢失的master节点超过半数,则可以使用新的集群处理工具elasticsearch-node unsafe-bootstrap pr37696 和elasticsearch-node detach-cluster pr37979j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

面对丢失半数master-eligible,es7.0.0(包含)版本之后的处理步骤如下:1 使用bin/elasticsearch-node unsafe-bootstrap命令让唯一主节点以不安全的方式改写投票节点,就像重新进行bootstrap一样,自己使用持久化的cluster state形成一个新集群2 其他数据节点无法加入新集群因为UUID不同(es使用UUID作为节点和集群的唯一表示,每个节点都会持久化当前集群的UUID),使用bin/elasticsearch-node detach-cluster命令让节点离开之前的集群3 启动数据节点和新的master-eligible节点(如下补充两个新的master-eligible),他会加入新集群中j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

cluster.initial_master_nodes:- {master-0}- {new-master-1}- {new-master-2}discovery.seed_hosts:- {master-ip-0}- {new-master-ip-1}- {new-master-ip-2}

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

场景2 master节点全部丢失

j8k28资讯网——每日最新资讯28at.com

es7.0.0版本之前

j8k28资讯网——每日最新资讯28at.com

1 关闭 security 功能(如果开启了, 最好先关闭security插件功能):j8k28资讯网——每日最新资讯28at.com

1.1 因为新启动的master节点, 没有数据节点(如果只配置了一个master的角色), security插件的初始化无法完成, 各类接口不好调用j8k28资讯网——每日最新资讯28at.com

1.2 如果给新启动的master节点, 配置了master and data角色, 则security插件会初始化成功. 会插入index, 但是这个index会和原来的data节点上保存的冲突. 不知道怎么解.elastic官方xpack-security;关闭鉴权:xpack.security.enabled:false2 启动足够的新master-eligible节点形成一个新集群.j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

discovery.zen.minimum_master_nodes: 2discovery.zen.ping.unicast.hosts:- {new-masters-1}- {new-masters-2}- {new-masters-3}

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

3 修改数据节点的为新master的地址,并且删除掉节点上的_state(因为新集群的cluster UUID不一致),同上j8k28资讯网——每日最新资讯28at.com

4 启动数据节点,数据被恢复加入到集群j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

es7.0.0(包含)版本之后

j8k28资讯网——每日最新资讯28at.com

已经没有cluster state了,唯一的希望是数据节点上的index数据;恢复方式借助elasticsearch-node 工具j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

1 关闭security功能(如果开启了, 最好先关闭security插件功能),原因同上j8k28资讯网——每日最新资讯28at.com

2 启动足够的新master-eligible节点形成一个新集群j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

cluster.initial_master_nodes:- {new-master-0}- {new-master-1}- {new-master-2}discovery.seed_hosts:- {new-master-ip-0}- {new-master-ip-1}- {new-master-ip-2}

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

3 bin/elasticsearch-node detach-cluster命令让数据节点离开之前的集群j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

./bin/elasticsearch-node detach-cluster------------------------------------------------------------------------    WARNING: Elasticsearch MUST be stopped before running this tool.------------------------------------------------------------------------You should only run this tool if you have permanently lost all of themaster-eligible nodes in this cluster and you cannot restore the clusterfrom a snapshot, or you have already unsafely bootstrapped a new clusterby running `elasticsearch-node unsafe-bootstrap` on a master-eligiblenode that belonged to the same cluster as this node. This tool can causearbitrary data loss and its use should be your last resort.Do you want to proceed?Confirm [y/N] yNode was successfully detached from the cluster

j8k28资讯网——每日最新资讯28at.com

4 查询dangling索引,GET /_dangling, 改api 引入es7.9版本于 pr581765 启动数据节点并使用Import dangling indexAPI将index数据import到cluster state中(官方推荐,es7.9版本之后). 或者 配置gateway.auto_import_dangling_indices: true引入于es7.6版本pr49174(es7.6.0-7.9.0可用该配置,在7.6版本之前不需要配置默认加载dangling索引)并启动数据节点j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

POST /_dangling/{index-uuid}?accept_data_loss=true

j8k28资讯网——每日最新资讯28at.com

6 导入完成之后,索引recovery之后即可进行读写j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

注意j8k28资讯网——每日最新资讯28at.com

Q1: 为什么7.6.0之后需要配置,才能处理悬空索引(dangling index)才能让数据加入新集群,7.6.0之后没有悬空索引吗?A1: 其实也是有的,只不过在es2版本将配置移除(对应pr10016),默认自动加载dangling index(es2.0-es7.6); 具体实现于org.elasticsearch.gateway.DanglingIndicesState#processDanglingIndices es7.6再次引入dangling配置,es7.9引入dangling index rest apij8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

Q2: 什么是 dangling 索引?A2: 当一个节点加入集群时,如果发现存储在其本地数据目录中的任何分片(shard)不存在于集群中,将认为这些分片属于“悬空”索引。悬空索引产生的场景(1)在 Elasticsearch 节点离线时删除了多个cluster.indices.tombstones.size 索引,节点再次加入集群集群 (2)master节点丢失,数据节点重新加入新的集群等j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

3.2 数据节点故障

j8k28资讯网——每日最新资讯28at.com

数据节点灾难故障之后,无法恢复加入集群;可将数据物理复制到新的节点,然后按照master节点丢失的方式,将数据节点加入集群即可。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

3.3 分片不能够自动分配

j8k28资讯网——每日最新资讯28at.com

查看索引分片为什么无法分配,POST_cluster/allocation/explainj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

3.3.1 分片正常

j8k28资讯网——每日最新资讯28at.com

如果分片数据正常,那么我们可以尝试重试分配分片任务;POST _cluster/reroute?retry_failedj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

获取索引的shard在那些节点上,使用_shard_stores apij8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

GET indexName1/_shard_stores

j8k28资讯网——每日最新资讯28at.com

使用cluster reroute重新分配j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

# 尝试分配副本 POST /_cluster/reroute{  "commands": [    {      "allocate_replica": {        "index": "{indexName1}",        "shard": {shardId},        "node": "{nodes-9}"      }    }  ]}

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

如果是主分片无法分配,可以尝试如下命令进行分配j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

POST /_cluster/reroute{  "commands": [    {      "allocate_stale_primary": {        "index": "{indexName1}",        "shard": {shardId},        "node": {nodes-9},        "accept_data_loss": true      }    }  ]}

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

如果主分片确实是无法分配,只能选择丢失该分片的数据,分配一个空的主分片j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

POST /_cluster/reroute{  "commands": [    {      "allocate_empty_primary": {        "index": "{indexName1}",        "shard": {shardId},        "node": "{nodes-9}",        "accept_data_loss": true      }    }  ]}

es5.0版本之前参考;https://www.elastic.co/guide/en/elasticsearch/reference/2.4/cluster-reroute.htmlj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

3.3.2 分片数据损坏

j8k28资讯网——每日最新资讯28at.com

shard corrupted

j8k28资讯网——每日最新资讯28at.com

错误参考Corrupted elastic indexj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

shard-tool es6.5版本引入,该操作需要stop节点elasticsearch-shard 工具es6.5版本引入 pr33848elasticsearch-shard remove-corrupted-data 的 es7.0.0引入 pr32281j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

bin/elasticsearch-shard remove-corrupted-data --index {indexName} --shard-id {shardId}## 示列:修复索引twitter的0号分片bin/elasticsearch-shard remove-corrupted-data --index twitter --shard-id 0## 如果--index和--shard-id换成索引分片目录参数--dir,则直接修复data和translogbin/elasticsearch-shard remove-corrupted-data --dir /var/lib/elasticsearchdata/nodes/0/indices/P45vf_YQRhqjfwLMUvSqDw/0

j8k28资讯网——每日最新资讯28at.com

修复完成之后,启动节点,如果分片不能够自动分配,使用reroute命令进行shard分片j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

POST /_cluster/reroute{  "commands":[    {      "allocate_stale_primary":{        "index":"index42",        "shard":0,        "node":"node-1",        "accept_data_loss":false      }    }  ]}

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

5版本之前可以通过索引级别配置,进行修复index.shard.check_on_startup: fix ,该配置在es6.5版本移除 pr32279j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

translog 损坏

j8k28资讯网——每日最新资讯28at.com

修复translog操作,需要stop节点。j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

修复工具 elasticsearch-translog es5.0.0 引入pr19342elasticsearch-shard remove-corrupted-data translog的 es7.4.1开始引入,pr47866elasticsearch-shard 可以直接清除translog,也可以像上文中指定--dir那样进行修复translogj8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

bin/elasticsearch-shard remove-corrupted-data --index  --shard-id   --truncate-clean-translog## 示列:修复索引twitter的0号分片bin/elasticsearch-shard remove-corrupted-data --index twitter --shard-id 0 --truncate-clean-translog

j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

清除完成之后使用cluster reroute 进行恢复j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

5版本之前可以通过索引级别配置,进行修复index.shard.check_on_startup: fix ,该配置在es6.5版本移除 pr32279j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

segments_N文件丢失

j8k28资讯网——每日最新资讯28at.com

该种场景的文件损坏是最难修复的;官方还未提供工具,我们正在自己调研中j8k28资讯网——每日最新资讯28at.com

j8k28资讯网——每日最新资讯28at.com

4 参考

j8k28资讯网——每日最新资讯28at.com

[1] elasticsearch集群启动流程j8k28资讯网——每日最新资讯28at.com

[2]https://www.elastic.co/guide/en/elasticsearch/reference/7.9/dangling-indices-list.htmlj8k28资讯网——每日最新资讯28at.com

[3]https://www.elastic.co/guide/en/elasticsearch/reference/7.10/node-tool.htmlj8k28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-81719-0.htmlElasticSearch集群灾难:别放弃,也许能再抢救一下

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 探索Python-Patterns模块:从设计模式到实际应用,助力编程效率提升!

下一篇: 深入解析并行编程利器:.NET中的 Parallel 类

标签:
  • 热门焦点
Top