Elasticsearch 的集群部署(十二)

这篇分析es的集群部署问题:
 
我们知道了 Elasticsearch 能够做什么,接下来我们将见识 Elasticsearch 另一 个很强的----- 扩展能力,也就是, Elasticsearch 如何能够处理更多的索引和搜索请 求,或者是更快地处理索引和搜索请求。在处理百万级甚至数十亿级的文档时, 扩展性是一个非常重要的因素。 没有了某种形式的扩展,在单一的 Elasticsearch 运行实例或节点( node) 上就无法一直支持规模持续增大的流量。 Elaticsearch 很 容易扩展。所以我们来了解 Elasticsearch 所拥有的扩展能力,以及如何使用这些 特性来给予 Elasticsearch 更多的性能、更多的可靠性。
理解物理设计
节点和分片
为了有个全局的理解,我们首先要知道 Elasticsearch 索引创建的时候,究竟 发生了什么,理解数据在物理是如何上组织的?
默认情况下,每个索引由 5 个主要分片组成,而每份主要分片又有一个副本, 一共 10 份分片。副本分片对于可靠性和搜索性能很有益处。技术上而言,一份 分片是一个目录中的文件,Lucene 用这些文件存储索引数据。分片也是 Elasticsearch 将数据从一个节点迁移到另一个节点的最小单位。 一个节点是一个 Elasticsearch 的实例。在服务器上启动 Elasticsearch 之后, 你就拥有了一个节点。如果在另一台服务器上启动 Elasticsearch, 这就是另一个 节点。甚至可以通过启动多个 Elasticsearch 进程,在同一台服务器上拥有多个节 点。
 
 
多个节点可以加入同一个集群。在多节点的集群上,同样的数据可以在多台 服务器上传播。这有助于性能,因为 Elasticsearch 有了更多的资源;这同样有助 于稳定性,如果每份分片至少有 1 个副本分片,那么任何一个节点都可以宕机, 而 Elasticsearch 依然可以进行服务,并返回所有数据。 对于使用 Elasticsearch 的应用程序,集群中有 1 个还是多个节点都是透明的。 默认情况下,可以连接集群中的任一节点并访问完整的数据集,就好像集群只有 单独的一个节点。 尽管集群对于性能和稳定性都有好处,但它也有缺点:必须确定节点之间能 够足够快速地通信,并且不会产生脑裂。为了解决这个问题,我们后面会来讨论。 一份分片是 Lucene 的索引:一个包含倒排索引的文件目录。倒排索引的结 构使得 Elasticsearch 在不扫描所有文档的情况下,就能告诉你哪些文档包含特定 的词条( 单词 )
Elasticsearch 索引和 Lucene 索引的对比:
Elasticsearch 索引被分解为多块 : 分片。一份分片是一个 Lucene 的索引,所以 一个 Elasticsearch 的索引由多个 Lucene 的索引组成。 一个分片是一个 Lucene 索引(一个倒排索引)。它默认存储原始文档的内 容,再加上一些额外的信息,如词条字典和词频,这些都能帮助到搜索。 词条字典将每个词条和包含该词条的文档映射起来。搜索的时候,Elastisearch 没有必要为了某个词条扫描所有的文档,而是根据这个字典快速地识别匹配的文 档。词频使得 Elasticsearch 可以快速地获取某篇文档中某个词条出现的次数。这 对于计算结果的相关性得分非常重要。例如,如果搜索“denver", 包含多个 “denver ” 的文档通常更为相 Elasticsearch 将给它们更高的得分,让它们出 现在结果列表的更前面。 接下来看看主分片和副本分片的细节,以及它们是如何 Elasticsearch 集群 中分配的。
附: ES 中的节点类型
Master-eligible nodes Master node
每个节点启动后 , 默认就是一个 Master eligible 节点,可以通过设置 node.master:false 来改变, Master-eligible 节点可以参加选主流程 , 成为 Master 节点,每个节点都保存了集群的状态 , 但只有 Master 节点才能修改集群的状态信 息,主节点主要负责集群方面的轻量级的动作,比如:创建或删除索引,跟踪集 群中的节点,决定分片分配到哪一个节点,在集群再平衡的过程中,如何在节点 间移动数据等。
Data Node
可以保存数据的节点 , 叫做 Data Node 。负责保存分片数据。在数据扩展上起 到了至关重要的作用,每个节点启动后 , 默认就是一个 Data Node 节点,可以通 过设置 node. data:false 来改变。
Ingest Node 可以在文档建立索引之前设置一些 ingest pipeline 的预处理逻辑,来丰富和 转换文档。每个节点默认启动就是 Ingest Node ,可用通过 node.ingest = false 禁用。
Coordinating Node
Coordinating Node 负责接收 Client 的请求 , 将请求分发到合适的节点 , 最终把 结果汇集到一起,每个节点默认都起到了 Coordinating Node 的职责,当然如果 master Data Ingest 全部禁用,那这个节点就仅是 Coordinating Node 节点了。
Machine Learning Node
用于机器学习处理的节点
主分片和副本分片
分片可以是主分片,也可以是副本分片,其中副本分片是主分片的完整副本, 副本分片可以用于搜索,或者是在原有主分片丢失后成为新的主分片。主分片是 权威数据,写过程先写主分片,成功后再写副分片。 Elasticsearch 索引由一个或多个主分片以及零个或多个副本分片构成。副本分片可以在运行的时候进行添加和移除,而主分片不可以。 可以在任何时候改变每个分片的副本分片的数量,因为副本分片总是可以被 创建和移除。这并不适用于索引划分为主分片的数量,在创建索引之前,你必须 决定主分片的数量。请记住,过少的分片将限制可扩展性,但是过多的分片会影 响性能。默认设置主分片的数量是 5 份。
复习
索引主分片的设置:
put test1{
"settings":{
"index.number_of_shards":3,
"index.codec":"best_compression"
     }
}
索引副本分片的设置:
put test1/_settings
{
"index.number_of_replicas":2
}
在集群中分发分片
最简单的 Elasticsearch 集群只有一个节点 : 一台机器运行着一个 Elasticsearch 进程。我们安装并启动了 Elasticsearch 之后,就已经建立了一个拥 有单节点的集群。随着越来越多的节点被添加到同一个集群中,现有的分片将在所有的节点中自动进行负载均衡。因此 , 在那些分片上的索引和搜索请求都可以 从额外增加的节点中获益。以这种方式进行扩展( 在节点中加入更多节点 ) 被称为
水平扩展。此方式增加更多节点,然后请求被分发到这些节点上,工作负载就被 分摊了。水平扩展的另一个替代方案是垂直扩展,这种方式为 Elasticsearch 的节点增 加更多硬件资源,可能是为虚拟机分配更多处理器,或是为物理机增加更多的内 存。尽管垂直扩展几乎每次都能提升性能, 它并非总是可行的或经济的。
分布式索引和搜索
在多个节点的多个分片上是如何进行索引和搜索的呢?
 
当索引一篇文档时
默认情况下,当索引一篇文档的时候,系统首先根据文档 ID 的散列值选择 一个主分片,并将文档发送到该主分片。这份主分片可能位于另一个节点,不过 对于应用程序这一点是透明的。 默认地,文档在分片中均匀分布:对于每篇文档,分片是通过其 ID 字符串 的散列决定的。每份分片拥有相同的散列范围,接收新文档的机会均等。 一旦目标主分片确定,接受请求的节点将文档转发到该主分片所在的节点。 随后,索引操作在该主分片的所有副本分片中进行。在所有可用副本分片完成文 档的索引后,索引命令就会成功返回。 这使得副本分片和主分片之间保持数据的同步。数据同步使得副本分片可以 服务于搜索请求,并在原有主分片无法访问时自动升级为主分片。
搜索索引时
当搜索一个索引时, Elasticsearch 需要在该索引的完整分片集合中进行查找。 这些分片可以是主分片,也可以是副本分片,原因是对应的主分片和副本分片通 常包含一样的文档。Elasticsearch 在索引的主分片和副本分片中进行搜索请求的 负载均衡,使得副本分片对于搜索性能和容错都有所帮助。在搜索的时候,接受请求的节点将请求转发到一组包含所有数据的分片Elasticsearch 使用 round-robin 的轮询机制选择可用的分片 ( 主分片或副本分片 ), 并将搜索请求转发过去,Elasticsearch 然后从这些分片收集结果,将其聚集为单 一的回复, 然后将回复返回给客户端应用程序。在默认情况下,搜索请求通过 round-robin 轮询机制选中主分片和副本分片。 向集群中加入节点
单机集群(伪集群)
创建 Elasticsearch 集群的第一步, 是为单个节点加入另一个节点 ( 或多个节 点) ,组成节点的集群。 没有加入第二个节点前:
http://xxxxxx:9200/_cluster/state/master_node,nodes?pretty
 
如何加入第二个节点:
1 、解压缩 压缩包 elasticsearch-7.7.0-linux-x86_64.tar.gzip 到另外一个目录, 比如 elasticsearch-2 ,并且保证这个 es 中不包含任何数据;
2 、修改配置文件
 
cluster.name 改为和第一个节点一样;
 
给第二个节点配置独立的名字;
 
 
network.host 改为本机地址
 
discovery.seed_hosts 改为本机地址 如果第一个节点有插件,则也需要安装同样的插件。 然后启动第二个节点即可。
现在有了第二个 Elasticsearch 节点加入了集群 , 可以再次
http://xxxxxx:9200/_cluster/state/master_node,nodes?pretty
 
新增节点上的分片是如何运作
那么新增节点上的分片是如何运作的呢 ? 看看向集群增加一个节点前后,索 引发生了些什么。在左端,test 索引的主分片全部分配到节点 Node1, 而副本分片” 分配没有地方分配。在这种状态下,集群是黄色的,因为所有的主分片有了安家 之处,但是副本分片还没有。
 
 
一旦第二个节点加入,尚未分配的副本分片就会分配到新的节点 Node2, 这 使得集群变为了绿色的状态。
 
当另一个节点加入的时候, Elasticsearch 会自动地尝试将分片在所有节点上 进行均匀分配。
 
 
如果更多的节点加入集群, Elasticsearch 将试图在所有的节点上均匀地配置 分片数量,这样每个新加入的节点都能够通过部分数据( 以分片的形式 ) 来分担负 载。将节点加入 Elasticsearch 集群带来了大量的好处,主要的收益是高可用性和 提升的性能。当副本分片是激活状态时( 默认情况下是激活的 ), 如果无法找到主分 片, Elasticsearch 会自动地将一个对应的副本分片升级为主分片。这样,即使失 去了索引主分片所在的节点,仍然可以访问副本分片上的数据。数据分布在多个 节点上同样提升了性能,原因是主分片和副本分片都可以处理搜索和获取结果的 请求。如此扩展还为整体集群增加了更多的内存,所以如果过于消耗内存的搜索
和聚集运行了太长时间或致使集群耗尽了内存,那么加入更多的节点总是一个处 理更多更复杂操作的便捷方式。
 
发现其他 ES 节点
集群的第二个节点是如何发现第一个节点、并自动地加入集群的,或者在集 群中有更多的节点的情况下,如何知道?
Elasticsearch 7.0 中引入的新集群协调子系统来处理这些事,采用的是单播 机制,这种机制需要已知节点的列表来进行连接。单播发现 ( unicast discovery ) Elasticsearch 连接一系列的主机,并试图发现 更多关于集群的信息。使用单播时,我们告诉 Elasticsearch 集群中其他节点的 IP 地址以及( 可选的 ) 端口或端口范围。 在 elasticsearch.yml 中通过 discovery.seed_hosts 配置种子地址列表,这样每 个节点在启动时发现和加入集群的步骤就是:
1 、去连接种子地址列表中的主机,如果发现某一个 Node Master Eligible Node,那么该 Master Eligible Node 会共享它知道的 Master Eligible Node ,这些 共享的 Master Eligible Node 也会作为种子地址的一部分继续去试探;
2 、直到找到某一个 seed addresss 对应的是 Master Node 为止;
3 、如果第二步没有找到任何满足条件的 Node ES 会默认每隔 1 秒后去重 新尝试寻找,默认为 1
4 、重复第三步操作直到找到满足条件为止,也就是直到最终发现集群中的 主节点,会发出一个加入请求给主节点
5 、获得整个集群的状态信息。
为什么实例需要知道集群状态信息?例如,搜索必须被路由到所有正确的分 片,以确保搜索结果是准确的。在索引或删除某些文档时,必须更新每个副本。 每个客户端请求都必须从接收它的节点转发到能够处理它的节点。每个节点都了 解集群的概况,这样它们就可以执行搜索、索引和其他协调活动。 discovery.seed_hosts 中的节点地址列表,可以包括集群中部分或者全部集群
节点,但是建议无论怎样都应该包含集群中 Master-eligible nodes 节点的部分或 者全部。
选举主节点
一旦集群中的节点发现了彼此,它们会协商谁将成为主节点。一个集群有一 个稳定的主节点是非常重要的,主节点是唯一一个能够更新集群状态的节点。主 节点一次处理一个群集状态更新,应用所需的更改并将更新的群集状态发布到群
集中的所有其他节点。 Elasticsearch 认为所有的节点都有资格成为主节点,除非某个节点的 node.master 选项设置为 false ,而 node.master 在不做配置的情况下,缺省为 true 。 如果完全使用默认配置启动新安装的 Elasticsearch 节点,它们会自动查找
在同一主机上运行的其他节点,并在几秒钟内形成集群。在生产环境或其他分布 式环境中还不够健壮。现在还存在一些风险:节点可能无法及时发现彼此,可能 会形成两个或多个独立的集群。从 Elasticsearch 7.0 开始,如果你想要启动一个 全新的集群,并且集群在多台主机上都有节点,那么你必须指定该集群在第一次 选举中应该使用的一组符合主节点条件的节点作为选举配置。这就是所谓的集群 引导,也可以称为集群自举,只在首次形成集群时才需要。 cluster.initial_master_nodes 这个参数就是用来设置一系列符合主节点条件 的节点的主机名或 IP 地址来进行集群自举。集群形成后,不再需要此设置,并 且会忽略它,也就是说,这个属性就只是在集群首次启动时有用。在向集群添加新的符合主节点条件的节点时不再需要任何特殊的仪式,只需 配置新节点,让它们可以发现已有集群,并启动它们。当有新节点加入时,集群 将会自动地调整选举配置。 在主节点被选举出来之后它会建立内部的 ping 机制来确保每个节点在集 群中保持活跃和健康,这被称为错误识别( fault detection), 有两个故障检测进程
在集群的生命周期中一直运行。一个是主节点的, ping 集群中所有的其他节点, 检查他们是否活着。另一种是每个节点都 ping 主节点,确认主节点是否仍在运 行或者是否需要重新启动选举程序。 在 Elasticsearch7 以前的版本中,为了预防集群产生脑裂 ( split brain) 的问题, Elasticsearch 6.x 及之前的版本使用了一个叫作 Zen Discovery 的集群协调子系 统。往往会将 discovery.zen.minimum_master_nodes 设置为集群节点数除以 2 再 加上 1 。例如, 3 个节点的集群 discovery.zen.minimum_master_nodes 要设置为 2,
而对于 14 个节点的集群 , 最好将其设置为 8
Elasticsearch 7 以后里重新设计并重建了的集群协调子系统,移除
minimum_master_nodes 参数,转而由集群自主控制。
什么是 Elasticsearch 的脑裂?
脑裂这个词描述了这样的场景 :( 通常是在重负荷或网络存在问题的情况 )Elasticsearch 集群中一个或多个节点失去了和主节点的通信,开始选举新的主 节点,并且继续处理请求。这个时候,可能有两个不同的 Elasticsearch 集群相互 独立地运行着,这就是“脑裂”一词的由来,因为单一的集群已经分裂成了两个 不同的部分,和左右大脑类似。为了防止这种情况的发生, Elasticsearch 7 以前 版本你需要根据集群节点的数量来设置 discovery. zen.minimum_master_nodes 如果节点的数量不变,将其设置为集群节点的总数 ; 否则将节点数除以 2 并加 1 是一个不错的选择,因为这就意味着如果个或多个节点失去了和其他节点的通信, 它们无法选举新的主节点来形成新集群,因为对于它们不能获得所需的节点 ( 成为主节点的节点 ) 数量 ( 超过一半 )
删除集群中的节点
添加节点是扩展的好方法,但是如果 Elasticsearch 集群中的一个节点掉线了 或者被停机了,那又会发生什么呢? 这里使用图 9-2 3 个节点的集群为例,其 中包含了测试的索引,5 个主分片和每个主分片对应的 1 个副本分片都分布在这 3 个节点上。 我们假设节点 1 宕机了,那么在节点 1 3 个分片怎么办 ? Elasticsearch 所 做的第 1 件事情是自动地将节点 2 上的 0 3 副本分片转为主分片。
 
这是由于索引操作会首先更新主分片,所以 Elasticsearch 要尽力使索引的主 分片正常运作。 注意 Elasticsearch 可以选择任一个副本分片并将其转变为主分片。只是在本 例中每个主分片仅有一个副本分片供选择,就是节点 Node2 上的副本分片。 副本分片变为主分片之后, 集群就会变为黄色的状态 , 这意味着某些副本分片 尚未分配到某个节点。Elasticsearch 下一步需要创建更多的副本分片来保持索引 的高可用性。由于所有的主分片现在都是可用的,节点 2 0 3 主分片的数据 会复制到节点 3 上作为副本分片,而节点 3 1 主分片的数据会复制到节点 2
 
一旦副本分片被重新创建,并用于弥补损失的节点,那么集群将重新回归绿 色的状态,全部的主分片及其副本分片都分配到了某个节点。请记住, 在这个时 间段内,整个集群都是可用于搜索和索引的,因为实际上没有丢失数据。 如果失去的节点多于 1 , 或者某个没有副本的主分片丢失了 , 那么集群就会 变为红色的状态,这意味着某些数据永远地丢失了,你需要让集群重连拥有丟失
数据的节点,或者对丢失的数据重新建立索引。 就副本分片的数量而言,你需要理解自己愿意承担多少风险,这一点非常重
要。有 1 份副本分片意味着集群可以缺失 1 个节点而不丢失数据。如果有 2 个副 本分片, 可以缺失 2 个节点而不丢失数据,以此类推。所以,确保你选择了合适 的副本数量。备份你的索引永远是个不错的主意。
停用节点
当节点宕机时,让 Elasticsearch 自动地创建新副本分片是很棒的选择。可是, 当集群进行例行维护的时候,你总是希望关闭某个包含数据的节点,而同时不让 集群进人黄色的状态。也许硬件过于老旧,或者处理的请求流量有所下降,总之你不再需要这么多节点了。可以通过杀掉 Java 进程来停止节点,然后让 Elasticsearch 将数据恢复到其他节点,但是如果你的索引没有副本分片的时候怎 么办? 这意味着,如果不预先将数据转移,关闭节点就会让你丢失数据 ! 值得庆幸的是,Elasticsearch 有一种停用节点 ( decommission) 的方式,告诉 集群不要再分配任何分片到某个或 1 组节点上。在 3 个节点的例子中,假设节点
1 、节点 2 和节点 3 IP 地址分别是 192.168.1.10 192.168.1.11 192.168.1.12 。 如果你想关闭节点 1 的同时保持集群为绿色状态,可以先停用节点,这个操作会 将待停用节点上的所有分片转移到集群中的其他节点。系统通过集群设置的临时 修改, 来为你实现节点的停用,
PUT _cluster/settings
{
"transient": {
"cluster.routing.allocation.exclude._name": "node-1"
     }
}
或者
PUT _cluster/settings
{
"transient": {
"cluster.routing.allocation.exclude._ip": "192.168.1.10"
      }
}
一旦运行了这个命令, Elaticsearch 将待停用节点上的全部分片开始转移到 集群中的其他节点上。 该过程可以重复,每次停止一个你想关闭的节点,或者也可以使用一个通过 逗号分隔的 IP 地址列表,一次停止多个节点。请记住,集群中的其他节点必须
有足够的磁盘和内存来处理分片的分配,所以在停止多个节点之前,做出相应的 计划来确保你有足够的资源。
扩展策略
将节点加入集群以增加性能,看上去很简单,但是稍微做些计划会使得你在 获取集群最佳性能的这条道路上走得更远。
Elasticsearch 的使用方式各有各的不同,所以需要根据如何索引和搜索数据, 为集群选择最佳的配置。通常来说,规划生产环境的 Elasticsearch 集群至少需要 考虑: 过度分片、将数据切分为索引和分片。
过度分片
让我们从过度分片开始说起。过度分片 ( over-sharding ) 是指你有意地为索引 创建大量分片,用于未来增加节点的过程。假设我们已经创建了拥有单一分片、 无副本分片的索引。但是,在增加了另外一个节点之后又会发生什么? 我们将得不到增加集群节点所带来的任何好处了。由于全部的索引和查询负 载仍然是由拥有单一分片的节点所处理,所以即使增加了一个节点你也无法进行
扩展。因为分片是 Elasticsearch 所能移动的最小单位,所以确保你至少拥有和集群 节点一样多的主分片总是个好主意。如果现在有一个 5 个节点、 11 个主分片的 集群,那么当你需要加入更多的节点来处理额外的请求时,就有成长的空间。使 用同样的例子,如果你突然需要多于 11 个的节点,就不能在所有的节点中分发 主分片,因为节点的数量将会超出分片的数量。 怎么办?创建一个有 10000 个主分片的索引? 一开始的时候,这看上去是 个好主意,但是 Elasticsearch 管理每个分片都隐含着额外的开销。每个分片都是 完整的 Lucene 索引,它需要为索引的每个分段创建一些文件描述符 , 增加相应的 内存开销。如果索引有过多的活跃分片, 可能会占用了本来支撑性能的内存,或 者触及机器文件描述符或内存的极限。对数据的压缩上也会有影响。
值得注意的是,没有对所有案例适用的完美分片索引比例。 Elasticsearch 选 择的默认设置是 5 个分片,对于普通的用例是不错的主意,但是考虑你的规划在 将来是如何增长( 或缩减 ) 所建分片的数量,这总是很件重要的事情。
不要忘记 : 一旦包含某些数量分片的索引被创建,其主分片的数量永远是不 能改变的。
Elasticsearch 索引能处理多大的数据 单一索引的极限取决于存储索引的机器之类型、你准备如何处理数据以及索 引备份了多少副本。 如何评估 ES 中的数据量是否合适呢?有几个参考值:
1 ES 官方推荐分片的大小是 20G - 40G ,最大不能超过 50G
2 、每个节点上可以存储的分片数量与可用的堆内存大小成正比关系,但是 Elasticsearch 并未强制规定固定限值。这里有一个很好的经验法则:确保对于节 点上已配置的每个 GB ,将分片数量保持在 20 以下。如果某个节点拥有 3GB 堆内存,那最多可有 60 个分片,那么有三个机器的集群, ES 可用总堆内存是 9GB ,则最多是 180 个分片,注意这个数据是包含了主副分片的。但是在此限值 范围内,设置的分片数量越少,效果就越好。
3 、通常来说,一个 Lucene 索引 ( 也就是一个 Elasticsearch 分片 ) 不能处理多 21 亿篇文档,或者多于 2740 亿的唯一词条, 但是在达到这个极限之前,你 可能就已经没有足够的磁盘空间了。
举例:三个机器的集群,总内存是 9GB ,准备 1 2 副,支持的总主分片数
量最大不宜超过 60 个分片。
将数据切分为索引和分片
现在还没有方法让我们增加或者减少某个索引中的主分片数量 , 但是你总是 可以对数据进行规划,让其横跨多个索引。这是另一种完全合理的切分数据的方 式。比如说以地理位置创建索引和分片,你可以为西藏索引创建 2 个主分片,而 为上海索引创建 10 个主分片,或者可以将数据以日期来分段,为数据按年份创 建索引: 2019 2020 2021 等。以这种方式将数据分段,对于搜索同样有所帮 助,因为分段将恰当的数据放在恰当的位置。如果顾客只希望搜索 2019 年和 2020 年的活动或分组,你只需要搜索相应的索引,而不是整个数据集中检索。 使用索引进行规划的另一种方式是别名。别名( alias ) 就像指向某个索引或一
组索引的指针。别名也允许你随时修改其所指向的索引。对于数据按照语义的方 式来切分, 这一点非常有用。你可以创建一个别名称为去年,指向 2019 ,当 2021 年 1 1 日到来 , 就可以将这个别名指向 2020 年的索引。 当索引基于日期的信息时( 就像日志文件 ) ,这项技术是很常用的,如此一来 数据就可以按照每月、每周、每日等日期来分段, 而每次分段过时的时候,“当
前”的别名永远可用来指向应该被搜索的数据,而无须修改待搜索的索引之名称。 此外,别名拥有惊人的灵活性,而且几乎没有额外负载,所以值得尝试。
 
这就是es集群的过程,下一篇我们分析集群中的健康检查和路由问题,敬请期待。
 
 
 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值