Elasticsearch 源码探究 001——故障探测和恢复机制

news2024/10/6 12:34:09

1、Elasticsearch 故障探测及熔断背景

探究Elasticsearch7.10.2 节点之间的故障探测以及熔断故障是怎么做的,思考生产上的最佳实践。

服务端故障场景:

  • 单个master挂掉

    • 除了断点断网,状态同步异常,主master也会认为自己已经失败,会退出,然后选举新的master

    • Elasticsearch 是一种基于点对点的系统,其中节点直接相互通信。主节点的职责是维护全局集群状态并在节点加入或离开集群时重新分配分片。每次集群状态更改时,新状态都会发布到集群中的所有节点。

    • 主master挂掉

    • 备master挂掉

  • 单个datanode挂掉

  • 单个datanode 和active master 同时挂掉

  • 服务端发生熔断

从服务端如何应对这些场景以及客户端如何应对这些场景。

2、集群故障探测认知

Elasticsearch 故障监测官方文档地址:

https://www.elastic.co/guide/en/elasticsearch/reference/7.10/cluster-fault-detection.html

leader check 和 follower check

7352fb71ff68da8a28517f79659a26e2.png

leader check 和 follower check 实际上都是线程,由 same 线程池执行,same 线程池是一种DIRECTl类型的线程池,当某个任务不需要在独立的线程执行,又想被线程池管理时,于是诞生了这种特殊类型的线程池:

在调用者线程中执行任务,这个same线程池是对用户不可见的,所以通过_cat/thread_pool看不到这个线程池。

map.put(Names.SAME, ThreadPoolType.DIRECT);

static final ExecutorService DIRECT_EXECUTOR = EsExecutors.newDirectExecutorService();

executors.put(Names.SAME, new ExecutorHolder(DIRECT_EXECUTOR, new Info(Names.SAME, ThreadPoolType.DIRECT)));

关于DirectExecutorService的关键定义:

@Override
public void execute(Runnable command) {
    command.run(); // 直接运行,并没有实际的线程池
    rethrowErrors(command);
}
  • follower check

    • 选出的主节点会定期检查集群中的每个节点,以确保它们仍然保持连接且健康

    • 对应实现在org.elasticsearch.cluster.coordination.FollowersChecker.FollowerChecker#handleWakeUp

  • leader检查

    • 集群中的每个节点也会定期检查选出的主节点的健康状况

    • 对应实现在:org.elasticsearch.cluster.coordination.LeaderChecker.CheckScheduler#handleWakeUp

相关配置

Elasticsearch 允许这些检查偶尔失败或超时而不采取任何行动。只有在连续多次检查失败后,才认为节点出现故障。

cluster.fault_detection. settings 相关配置如下:

  • cluster.fault_detection.follower_check.interval

    • 静态

    • 设置follower_checker的间隔, 默认1s一次

  • cluster.fault_detection.follower_check.timeout

    • 静态

    • follower_checker的超时时间,默认10s

  • cluster.fault_detection.follower_check.retry_count

    • 静态

    • follower_check 失败多少次会认为follower 检测失败, 默认3次, 超过这个次数之后,当选的主节点认为该节点出现故障并将其从集群中删除

  • cluster.fault_detection.leader_check.interval

    • 静态

    • 设置leader_checker的间隔, 默认1s一次

  • cluster.fault_detection.leader_check.timeout

    • 静态

    • leader_checker的超时时间,默认10s

  • cluster.fault_detection.leader_check.retry_count

    • 静态

    • leader_check 失败多少次会认为leader检测失败, 默认3次, 超过这个次数之后,节点认为当选的主节点有故障并尝试查找或选举新的主节点

💡  注意事项:

选出的主节点检测到某个节点已断开连接,这种情况会被立即认为是故障,主节点绕过超时和重试设置值并尝试从集群中删除节点。

类似地,如果节点检测到选出的主节点已断开连接,则这种情况将被视为立即故障。节点绕过超时和重试设置并重新启动其发现阶段以尝试查找或选举新的主节点。

3、选举认知

因为 master节点挂掉的时候,可能有两种情况, 主master挂掉和备master挂掉。如果主master挂掉就会触发选举。

所以分析一下相关选举配置如下:

  • discovery.cluster_formation_warning_timeout

    • 静态

    • 多久没选举完成就会打印出”master not discovered”

  • discovery.find_peers_interval

    • 静态

    • 设置节点在尝试另一轮discovery选举 之前等待的时间。

  • discovery.probe.connect_timeout

    • 静态

    • 设置尝试连接到每个地址时等待的时间。默认为 3 秒

  • discovery.probe.handshake_timeout

    • 静态

    • 设置尝试通过握手识别远程节点时等待的时间,默认为 1 秒。

  • discovery.request_peers_timeout

    • 静态

    • 设置节点认为向对等方发送请求失败之前的超时时间,默认3s

  • discovery.seed_resolver.max_concurrent_resolvers

    • 静态

    • 指定解析种子节点地址时要执行的并发 DNS 查找数量。默认为 10。

  • discovery.seed_resolver.timeout

    • 静态

    • 指定解析种子节点地址时执行的每次 DNS 查找的等待时间。默认为 5 秒。

  • cluster.auto_shrink_voting_configuration

    • 动态

    • 控制投票配置是否自动删除离开的节点,只要它仍然包含至少 3 个节点。默认值是true。如果设置为 false,投票配置永远不会自动收缩。

  • cluster.election.back_off_time

    • 静态

    • 设置每次选举失败后选举前等待时间上限的增加量。请注意,这是线性退避。默认为 100 毫秒。更改此默认设置可能会导致集群无法选择主节点。

  • cluster.election.duration

    • 静态

    • 设置每次选举在节点认为失败并安排重试之前允许进行的时间。默认为 500 毫秒。更改此默认设置可能会导致集群无法选择主节点。

  • cluster.election.initial_timeout

    • 静态

    • 设置节点最初等待的时间上限,或者在当选的主节点失败后,在尝试第一次选举之前等待的时间上限。默认为 100 毫秒。更改此默认设置可能会导致集群无法选择主节点。

  • cluster.election.max_timeout

    • 静态

    • 设置节点在尝试第一次选举之前等待的时间的最大上限,以便长时间持续的网络分区不会导致选举过于稀疏。默认为 10 秒。更改此默认设置可能会导致集群无法选择主节点。

  • cluster.no_master_block

    • 动态

    • 指定当集群中没有活动主节点时拒绝哪些操作。此设置具有三个有效值.

    • all , 节点上的所有操作(读和写操作)都会被拒绝。这也适用于 API 集群状态读取或写入操作

    • write (默认) 写入操作被拒绝。根据最后已知的集群配置,读取操作成功。这种情况可能会导致部分读取过时数据,因为该节点可能与集群的其余部分隔离。

    • metadata_write, 仅元数据写入操作(例如映射更新、路由表更改)被拒绝,但常规索引操作继续工作。根据最后已知的集群配置,读取和写入操作成功。这种情况可能会导致部分读取过时数据,因为该节点可能与集群的其余部分隔离

4、分片主从切换是如何做的

在节点被判断离开集群的时候,会触发一个node-left的状态更新任务。

this.followersChecker = new FollowersChecker(settings, transportService, this::onFollowerCheckRequest, this::removeNode,
            nodeHealthService);

private void removeNode(DiscoveryNode discoveryNode, String reason) {
        synchronized (mutex) {
            if (mode == Mode.LEADER) {
        // 提交一个node-left状态更新任务
                masterService.submitStateUpdateTask("node-left",
                    new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason),
                    ClusterStateTaskConfig.build(Priority.IMMEDIATE),
                    nodeRemovalExecutor,
                    nodeRemovalExecutor);
            }
        }
    }

之后就进入到状态更新阶段:org.elasticsearch.cluster.service.MasterService#runTasks:

b80e29f5a351a90357809addd2384ff8.png

这里对应的线程池为 org.elasticsearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor,于是进入到其的execute方法:

@Override
    public ClusterTasksResult<Task> execute(final ClusterState currentState, final List<Task> tasks) throws Exception {
        final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes());
        boolean removed = false;
    // 如果任务指定的节点存在于当前集群状态中,则从DiscoveryNodes.Builder对象中删除该节点,
    // 并将removed标志设置为true。如果该任务指定的节点不存在于当前集群状态中,则记录一条调试级别的日志,表示忽略该任务。
        for (final Task task : tasks) {
            if (currentState.nodes().nodeExists(task.node())) {
                remainingNodesBuilder.remove(task.node());
                removed = true;
            } else {
                logger.debug("node [{}] does not exist in cluster state, ignoring", task);
            }
        }
    // 如果没有删除任何节点,则返回当前的集群状态
        if (!removed) {
            // no nodes to remove, keep the current cluster state
            return ClusterTasksResult.<Task>builder().successes(tasks).build(currentState);
        }
    // 剩余节点
        final ClusterState remainingNodesClusterState = remainingNodesClusterState(currentState, remainingNodesBuilder);

        return getTaskClusterTasksResult(currentState, tasks, remainingNodesClusterState);
    }

protected ClusterTasksResult<Task> getTaskClusterTasksResult(ClusterState currentState, List<Task> tasks,
                                                                 ClusterState remainingNodesClusterState) {
        ClusterState ptasksDisassociatedState = PersistentTasksCustomMetadata.disassociateDeadNodes(remainingNodesClusterState);
        final ClusterTasksResult.Builder<Task> resultBuilder = ClusterTasksResult.<Task>builder().successes(tasks);
        return resultBuilder.build(allocationService.disassociateDeadNodes(ptasksDisassociatedState, true, describeTasks(tasks)));
    }

随后调用 org.elasticsearch.cluster.routing.allocation.AllocationService#disassociateDeadNodes(org.elasticsearch.cluster.ClusterState, boolean, java.lang.String)

private void disassociateDeadNodes(RoutingAllocation allocation) {
        for (Iterator<RoutingNode> it = allocation.routingNodes().mutableIterator(); it.hasNext(); ) {
            RoutingNode node = it.next();
      // 如果该节点的ID存在于RoutingAllocation对象的数据节点列表中,则表示该节点是活动节点,继续遍历下一个节点
            if (allocation.nodes().getDataNodes().containsKey(node.nodeId())) {
                // its a live node, continue
                continue;
            }
      // 否则,这个节点就离线。
      // 对于每个已离线的节点,该方法遍历该节点上的所有分片,并为每个分片创建一个UnassignedInfo对象,表示该分片未分配的原因是节点离线。
            // 然后,该方法调用failShard方法,将该分片标记为失败,并将其移动到未分配状态。该方法还会从RoutingNode列表中删除已离线的节点
            // now, go over all the shards routing on the node, and fail them
            for (ShardRouting shardRouting : node.copyShards()) {
                final IndexMetadata indexMetadata = allocation.metadata().getIndexSafe(shardRouting.index());
                boolean delayed = INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.get(indexMetadata.getSettings()).nanos() > 0;
                UnassignedInfo unassignedInfo = new UnassignedInfo(UnassignedInfo.Reason.NODE_LEFT, "node_left [" + node.nodeId() + "]",
                    null, 0, allocation.getCurrentNanoTime(), System.currentTimeMillis(), delayed, AllocationStatus.NO_ATTEMPT,
                    Collections.emptySet());
                allocation.routingNodes().failShard(logger, shardRouting, unassignedInfo, indexMetadata, allocation.changes());
            }
            // its a dead node, remove it, note, its important to remove it *after* we apply failed shard
            // since it relies on the fact that the RoutingNode exists in the list of nodes
            it.remove();
        }
    }

org.elasticsearch.cluster.routing.RoutingNodes#failShard, 将分片移动到未分配状态或完全删除分片(如果是重分配relocation目标分片),具体的逻辑如下:

  • 如果分片是主分片,则会先失败初始化副本

  • 如果分片是活动主分片,则会将一个活动副本升级为主分片(如果存在这样的副本)

  • 如果分片是正在分配relocation的主分片,则会删除主分片重分配relocation目标分片

  • 如果分片是正在重分配relocation的副本,则会将副本重分配relocation目标升级为完全初始化的副本,并删除重定位源信息。这是因为对等恢复始终是从主分片进行的。

  • 如果分片是(主分片或副本)分配relocation目标,则还会清除源分片上的重分配relocation信息。

这里我们关注一下,怎么做的主从切换, 具体进入到如下逻辑:

// fail actual shard
        if (failedShard.initializing()) {
            if (failedShard.relocatingNodeId() == null) {
                if (failedShard.primary()) {
                    // promote active replica to primary if active replica exists (only the case for shadow replicas)
                    unassignPrimaryAndPromoteActiveReplicaIfExists(failedShard, unassignedInfo, routingChangesObserver);
                } else {
                    // initializing shard that is not relocation target, just move to unassigned
                    moveToUnassigned(failedShard, unassignedInfo);
                }
            }

org.elasticsearch.cluster.routing.RoutingNodes#unassignPrimaryAndPromoteActiveReplicaIfExists

private void unassignPrimaryAndPromoteActiveReplicaIfExists(ShardRouting failedShard, UnassignedInfo unassignedInfo,
                                                                RoutingChangesObserver routingChangesObserver) {
        assert failedShard.primary();
    // 这里会从所有副本分片中找最高版本的副本
        ShardRouting activeReplica = activeReplicaWithHighestVersion(failedShard.shardId());
        if (activeReplica == null) {
            moveToUnassigned(failedShard, unassignedInfo);
        } else {
      // 将主分片移动到未分配状态,并将其降级为副本, 这里还会将主分片加入到unassignedShards列表中, 根据索引的配置会做分片恢复
            movePrimaryToUnassignedAndDemoteToReplica(failedShard, unassignedInfo);
      // 将活动副本升级为主分片,并通知路由变更观察器进行相应的处理
            promoteReplicaToPrimary(activeReplica, routingChangesObserver);
        }
    }

做好路由的变更之后,master会同步集群状态给各个节点,至此就完成了主副本分片切换。

总的来说,只要探测到data-node离线肯定就会进行主从切换,所以这部分的耗时主要在同步集群状态上,在网络等资源良好的情况下,基本是秒级的。

5、客户端重试机制

以 java 客户端为例,当配置多个 IP , 客户端会自动执行负载均衡, 默认是轮询存活的节点。

意味着每个新的请求都会发送到下一个 IP 地址,当所有的 IP 地址都被使用后,再从头开始。

这样可以确保所有的 Elasticsearch 节点都有均等的机会处理请求,避免了某个节点过载的问题。

当一个节点失败了,会加入到blaklist:

private void onFailure(Node node) {
    while(true) {
        DeadHostState previousDeadHostState =
            blacklist.putIfAbsent(node.getHost(), new DeadHostState(DeadHostState.DEFAULT_TIME_SUPPLIER));
        if (previousDeadHostState == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("added [" + node + "] to blacklist");
            }
            break;
        }
        if (blacklist.replace(node.getHost(), previousDeadHostState,
                new DeadHostState(previousDeadHostState))) {
            if (logger.isDebugEnabled()) {
                logger.debug("updated [" + node + "] already in blacklist");
            }
            break;
        }
    }
    failureListener.onFailure(node);
}

在发送请求的时候,selectNodes 会过滤出活跃的节点(没有被列入黑名单或已经到了重试时间的节点),然后使用一个 NodeSelector 对象对这些节点进行选择。

可以看出是轮询的方式。

static Iterable<Node> selectNodes(NodeTuple<List<Node>> nodeTuple, Map<HttpHost, DeadHostState> blacklist,
                                      AtomicInteger lastNodeIndex, NodeSelector nodeSelector) throws IOException {
        /*
         * Sort the nodes into living and dead lists.
         */
        List<Node> livingNodes = new ArrayList<>(Math.max(0, nodeTuple.nodes.size() - blacklist.size()));
        List<DeadNode> deadNodes = new ArrayList<>(blacklist.size());
        for (Node node : nodeTuple.nodes) {
            DeadHostState deadness = blacklist.get(node.getHost());
      // 没有被列入黑名单或已经到了重试时间的节点 , 默认是1min重试间隔, 只有1min之后才会再次访问这个节点, 无法修改,写死的
            if (deadness == null || deadness.shallBeRetried()) {
                livingNodes.add(node);
            } else {
                deadNodes.add(new DeadNode(node, deadness));
            }
        }

注意:一些客户端有“节点嗅探”功能,但是这种在 k8s 场景下并不友好,而且违背了仅协调节点的部署架构,对以后 VIP 规划也有影响,所以并不太建议开启。

6、故障梳理

注:这里的挂掉都是指网卡挂了,并不是被kill或者关机,这里两者有本质区别:

  • 网卡下线,需要探测时间。

  • 如果进程被kill ,被kill的es 节点会立即断开连接,不需要探测时间,会被立即移出集群,从而触发node-left,故障恢复

这里搭建 6 个节点, 其中 3 个节点为 master 角色, 3 个节点为 data 角色,使用的是虚拟机部署

6.1 故障一:主 master 挂掉

a04ab4d5248e6c917664ecf3eeac0184.png

第一次检测失败:2023-07-14T20:40:52

6b305173a6104395a16c57fc7dc19641.png

第二次检测失败:2023-07-14T20:41:03

0278e9668e656f18c17cb1dcaa6aa5a9.png

第三次检测失败:2023-07-14T20:41:14

b90fa382c5c2f2f102b0e715542846de.png

2023-07-14T20:41:14,427 选举结束,再发布集群状态。

3cce5b98860b7d0b4c178e7ed338728b.png

值得注意的是,客户端不断使用非挂掉机器的ip 访问es,  只有选举期间是无法写入的,其他时间都可以写入,但是无法更新集群状态(比如创建索引等),因为这个时候master已经挂了。

6.2 故障二:备master挂掉

5b1fa27b773308d32458789ca2233e2e.png

直接看最后一次的 timeout:2023-07-14T21:04:54 ,耗时就是大概30s。

c28868dc094a274c22618067f256e703.png

值得注意的是,客户端不断使用非挂掉机器的ip 访问es,  因为只是关闭了备master节点,所以期间集群状态一直都是green的。对客户端新的请求是没有影响的。

6.3 故障三:单个datanode挂掉

4c47086f25169807a6586319c983ea88.png b17faa68c55b5155ec74f71d0ce751dd.png

直接看最后一次check失败:2023-07-14T21:17:58 , 相差大概也是30s,这个时候标记这个数据节点的分片为failed(会做主从分片切换), 并计划恢复那些丢失的分片,这个时候集群为Yellow状态

eaddb18b332aadab14e03fc29acd72e9.png f10086f72d31d5310a7ade80a22add40.png

由于使用的是默认配置index.unassigned.node_left.delayed_timeout  为1m, 而且索引的数据量很小,所以集群很快恢复了分片,转入到GREEN状态

afa7e5582a6ca3a58e9870007a3f15ce.png

6.4 故障四:活跃master节点和一个datanode 同时挂掉

这个场景其实是故障一和故障三的结合, 从服务端流程可以看出来,先进行了master重选举,再然后是处理数据节点的离线。原因很简单,因为只有active master 节点才有follower 检测,所以有这个顺序。

1d4bbe8dc2acc545e2636becdafc2ee3.png 1f75c8cee7c3f8cb5a41870b6647c835.png

2023-07-14T21:34:54 选举出新 master

610f7417bc701054928d08668f756098.png

2023-07-14T21:35:26 探测到 data 节点

972a90bb0c67f2952e4770330a0eeb10.png

6.5 故障五:服务端熔断

这块主要看客户端怎么做的容错,以java客户端为例,

<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
  <version>7.10.2</version>
</dependency>

在服务端熔断的情况下,服务端会直接返回异常,客户端会抛出异常, 这个时候需要判断返回码429, 并判断熔断类型是否为TRANSIENT, 如果是需要不断重试, 如果是PERMANENT 则可以放弃重试。

2a797dd4b532a057f07efa75d50eb037.png

6.6 故障恢复流程图

7bc7de94a111fd0293b433d7d791ca78.png

7、故障总结

注:这里的挂掉都是指网卡挂了,并不是被 kill 或者关机,这里两者有本质区别:

  • 网卡下线,需要探测时间。

  • 如果进程被 kill ,被 kill 的 es 节点会立即断开连接,不需要探测时间,会被立即移出集群,从而触发 node-left,故障恢复

故障场景客户端影响大概恢复时长优化手段以及对应时长
场景一:单个 active master 挂掉1.访问故障节点的请求都失败

探测active 故障之前:
2.可对已有索引进行读写,但是无法更新集群状态,比如创建索引等

选举期间:
3.其他读请求可以成功进行,但是可能会读到旧数据
1. 取决故障节点恢复时间, 这个对客户端没影响,只要客户端能重试请求到其他存活es节点就可以

2.探测master挂掉需要30s, 期间可读写索引,但是无法更新集群状态

3. 3个master节点 选举只需要几s,具体看网络环境和节点个数,未选举出新的master时,只能读取
可以降低节点的探测超时时间,改成每次探测超时时间为3s,而不是10s,这样就可以将探测时间降低到9s
场景二:单个备用master挂掉1.访问故障节点的请求都失败1. 取决故障节点恢复时间, 这个对客户端没影响,只要客户端能重试请求到其他存活es节点就可以,不会发生重选举
场景三:单个data node挂掉1.访问故障节点的请求都失败

2. 访问这个数据节点的分片的请求都失败
1. 取决故障节点恢复时间, 这个对客户端没影响,只要客户端能重试请求到其他存活es节点就可以

2.需要探测数据节点掉线30s,所以30s之内的这个数据节点负责的分片都无法写入和读取。

 3.主副本分片切换之后,同步集群状态大概几s,具体看网络环境和节点个数
可以降低节点的探测超时时间,改成每次探测超时时间为3s,而不是10s,这样就可以将探测时间降低到9s
场景四:active master节点和data node 节点同时挂掉场景一和场景三的结合,会先经历场景一,然后发生场景三1.探测master挂掉需要30s, 期间可读写索引,但是无法更新集群状态,选举需要秒级时间(3个master在集群压力小的情况下,通常1s之内)

2.需要探测数据节点掉线30s,所以30s之内的这个数据节点负责的分片都无法写入
可以降低节点的探测超时时间,改成每次探测超时时间为3s,而不是10s,这样就可以将探测时间降低到18s
场景五:服务端熔断1. 访问熔断节点的请求都失败1.取决服务端内存释放情况,如果只是临时熔断,理论上几s钟就能释放一些客户端应该判断服务端异常是否为熔断,如果是临时熔断应该做重试处理,理论上重试多少次都没关系,只要临时熔断,总会自动恢复

8、最佳实践思考总结

8.1 客户端实践复盘

  • 客户端地址不能只设置成1个, 尤其是在虚拟机部署的es集群情况下,这种情况下一旦设置的 ip 地址挂了,就算 es 集群恢复,业务也无法恢复, 就算是 k8s 部署的 es 节点也需要填写多个 k8s master 地址 + svc nodeport。

    • 如果是k8s的部署场景,需要注意pod的健康检测,不应该仅仅只是探测自身服务,还要探测和其他节点的网络连通性。

    • 想象发生了故障二,理论上是对业务没有影响的,但是如果只单纯 curl 127.0.0.1:9200 的话,这样会造成svc的endpoint里较长时间还保留着已经挂掉的 pod ip。k8s在负载均衡的时候就会将流量转发到出问题的 pod,这样就会导致请求失败,实际上对于es服务来讲,几乎对客户端没有影响。

  • 客户端需要参考 java sdk 做容错处理(如果该语言sdk没有实现的话), 实现配置多个节点ip,划分2个列表,存活列表和黑名单,对于存活列表轮询访问,失败过的节点就加到黑名单,一段时间之后(1,2,4s…退避式)重试。

  • 在服务端熔断的情况下,服务端会直接返回异常,客户端会抛出异常, 需要客户端对这个异常进行cath 然后重试。这个时候需要判断返回码429, 并判断熔断类型是否为TRANSIENT, 如果是需要不断重试, 如果是PERMANENT 则可以放弃重试。

8.2 服务端实践复盘

  • 默认的故障探测耗时为30s, 耗时较长, 在网络等资源良好的情况下,可以考虑缩短超时。

  • 默认情况下,这个配置index.unassigned.node_left.delayed_timeout 为1m, 太短了,一般都有systemctl或者 k8s pod 漂移保活,这个可以考虑设置较长时间,比如1d,另外可以考虑索引的这个配置设置的都一样,不然会造成大量索引同时恢复,对es稳定性造成影响。

作者介绍

He Chengbo,互联网安全独角兽公司资深工程师,死磕 Elasticsearch 星球资深活跃技术专家。

在此,感谢铭毅老师提供这个宝贵的平台发表文章,也感谢您给予的指导和鼓励!

推荐阅读

  1. 全网首发!从 0 到 1 Elasticsearch 8.X 通关视频

  2. 重磅 | 死磕 Elasticsearch 8.X 方法论认知清单

  3. 如何系统的学习 Elasticsearch ?

  4. 2023,做点事

90ec05795b8fbb27255732cccdb09bb1.jpeg

更短时间更快习得更多干货!

和全球 近2000+ Elastic 爱好者一起精进!

714b9839742d991f7b5914a56c2efa4a.gif

大模型时代,抢先一步学习进阶干货!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASPICE V模型之软件需求

ASPICE V模型之软件需求 了解ASPICE认识软件需求软件需求分解软件需求工作流程 了解ASPICE ASPICE全称是“Automotive Software Process Improvement and Capacity Determination”汽车软件过程改进及能力评定&#xff0c;是汽车行业用于评价软件开发团队的研发能力水平的模型框…

全球生成式AI大竞赛,Llama 2大模型现已可在亚马逊云科技上使用

一直以来Llama可以说是AI社区内最强大的开源大模型。但因为开源协议问题&#xff0c;一直不可免费商用。7月19日&#xff0c;Meta发布了大家期待已久的免费可商用版本Llama 2。一夜之间&#xff0c;大模型格局再次发生巨变。 作为Meta宣布的首批合作伙伴之一&#xff0c;现亚…

Ubuntu 安装Postgresql与PostGIS

1.前言 最近在做GIS分析&#xff0c;采集设备的经纬度点判断是否进出围栏以及是否产生道路偏移报警&#xff0c;在之前的文章有介绍过Windows下使用C#来实现&#xff0c;参考文章&#xff1a;利用PostgresqlPostgis进行空间地理信息分析&#xff08;道路偏移&#xff0c;进出电…

sql注入---报错注入

updatexml&#xff08;&#xff09;&#xff1a;对XML文档数据进行查询和修改 extractvalue&#xff08;&#xff09;&#xff1a;对XML文档数据进行查询 floor&#xff08;&#xff09;&#xff1a;取整的函数 前提是未关闭数据库报错函数&#xff0c;对于一些SQL语句的错误…

Java反射、动态代理

文章目录 反射什么是动态代理&#xff1f;程序为什么需要代理?代理长什么样? 反射 java反射机制原理&#xff1a;我们写的源代码是.java文件&#xff0c;通过javac编译后成为.class文件&#xff0c;即字节码文件&#xff0c;程序执行时&#xff0c;JVM会类加载字节码文件到内…

浅谈单片机

目录 1.什么是单片机 2.单片机的作用&#xff1f; 3.单片机的种类 4.如何学好单片机 5. 单片机的就业前景 1.什么是单片机 单片机&#xff08;Microcontroller Unit&#xff0c;简称MCU&#xff09;是一种集成了微处理器核心、存储器、输入输出端口和各种外围功能模块于一体…

Rspack 学习了解

一、简介 Rspack GitHub 仓库、Rspack Quick start。 Rspack 是由字节 ByteDance Web Infra 团队基于 Rust 语言开发的 Web 高性能构建工具。 Rust 是种高效、可靠的通用高级语言。其高效不仅限于开发效率&#xff0c;执行效率也是令人称赞的&#xff0c;属于少有兼顾开发效率…

vulnhub打靶--buli_b0x

目录 vulnhub--buli_b0x1.下载靶机2.导入靶机&#xff0c;开启靶机&#xff0c;nmap扫描3.探测目录&#xff0c;发现敏感目录4.通过test.php下载源码5.代码审计6.提权7.总结 vulnhub–buli_b0x 1.下载靶机 Billu_b0x.zip 2.导入靶机&#xff0c;开启靶机&#xff0c;nmap扫描…

分布式 RPC 框架HSF

分布式 RPC 框架HSF 概述HSF架构调用方式优势应用场景 概述 HSF (High-speed Service Framework)&#xff0c;高速服务框架&#xff0c;是在阿里巴巴内部广泛使用的分布式 RPC 服务框架。HSF 作为阿里巴巴的基础中间件&#xff0c;联通不同的业务系统&#xff0c;解耦系统间的…

WebSocket笔记

1. websocket介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c; 并进行双向数据传输。 HTTP协议和WebSocket协议对比&#xff1a; HTTP是短连接W…

iOS--动静态库

文章目录 认识动静态库静态库动态库静态的打包静态库的使用动态库的打包动态库的使用 动静态库的本质就是可执行程序的"半成品"。 需要完成一个可执行程序需要经历以下四个步骤: 预处理:完成头文件的展开&#xff0c;去掉注释&#xff0c;宏替换&#xff0c;条件编译…

微信小程序学习笔记(五)——优化

下拉刷新后主动关闭 Page({onPullDownRefresh: function() {// ...wx.stopPullDownRefresh()} })在发起请求时设置 loading&#xff0c;请求结束后关闭 Page({onLoad: function(options) {wx.showLoading({title: 数据加载中...}) // 展示 loadingwx.request({// ...complete: …

leetcode每日一练-第206题-反转链表

一、思路 迭代 二、解题方法 以输入为 1 -> 2 -> 3 -> 4 -> 5 的链表为例 三、code class Solution { public:ListNode* reverseList(ListNode* head) {ListNode* prevnullptr;//反转后的链表ListNode* currhead;//当前更新的原有链表while(curr)//原有链表无值…

【大模型】更强的 LLaMA2 来了,开源可商用、与 ChatGPT 齐平

【大模型】可商用且更强的 LLaMA2 来了 LLaMA2 简介论文GitHubhuggingface模型列表训练数据训练信息模型信息 许可证参考 LLaMA2 简介 2023年7月19日&#xff1a;Meta 发布开源可商用模型 Llama 2。 Llama 2是一个预训练和微调的生成文本模型的集合&#xff0c;其规模从70亿到…

中间件安全-CVE漏洞复现-Weblogic+JBoss+GlassFish

服务攻防测试流程&#xff1a; 使用vulfocus靶场&#xff1a; 案例演示&#xff1a;中间件-Weblogic-工具梭哈 探针默认端口&#xff1a;7001&#xff0c;Weblogic是Oracle公司推出的J2EE应用服务器 使用vulfocus靶场复现漏洞 漏洞&#xff1a;weblogic-cve_2020_14883 启动环…

自然语言处理:赋予AI理解和交流的能力

文章目录 &#x1f340;引言&#x1f340;NLP的定义与重要性&#x1f340;NLP的应用领域&#x1f340;学好自然语言处理需要掌握以下知识&#x1f340;GPT和自然语言处理&#x1f340;总结 &#x1f340;引言 自然语言处理&#xff08;Natural Language Processing&#xff0c;…

Visual Studio创建Web项目时候报错- 找不到“2.0.1“版本的程序包”解决方法

问题描述 在今天我开始想做一个ASP.Net Web 项目时&#xff0c;在创建项目的时候突然报下面这个错&#xff0c;也是试了很多方法&#xff0c;比如卸载重新安装&#xff0c;安装更高版本&#xff0c;我之前用的是2019版本&#xff0c;后面下载了2022的&#xff0c;又出现了新的…

python与深度学习(三):ANN和fashion_mnist

目录 1. 说明2. fashion_mnist实战2.1 导入相关库2.2 加载数据2.3 数据预处理2.4 数据处理2.5 构建网络模型2.6 模型编译2.7 模型训练2.8 模型保存2.9 模型评价2.10 模型测试2.11 模型训练结果的可视化 3. fashion_mnist的ANN模型可视化结果图4. 完整代码 1. 说明 本篇文章是A…

(202307)wonderful-sql:环境搭建(ubuntu 22.04 + mysql 8.0 + workbench/dbeavar,免密码-不建议免哈)

前言 能够再次参加datawhale组织的开源学习是十分兴奋的&#xff0c;看到datawhale能够越办越好我也是由衷地感到高兴。 这次参加的是mysql的学习&#xff0c;我知道这样短期的学习不会对我的能力造成多么大的提升&#xff0c;但是相信经过这次学习&#xff0c;我又将被datawh…

Kubernetes中Pod的扩缩容介绍

Kubernetes中Pod的扩缩容介绍 在实际生产系统中&#xff0c;我们经常会遇到某个服务需要扩容的场景&#xff0c;也可能会遇到由于资源紧张或者工作负载降低而需 要减少服务实例数量的场景。此时可以利用 Deployment/RC 的 Scale 机制来完成这些工作。 Kubernetes 对 Pod 的扩…