分布式锁与ZooKeeper的探索之旅

一、引言

在现代的软件开发中,分布式系统扮演着越来越重要的角色。随着系统的扩展和复杂度的增加,确保多个进程或服务之间的数据一致性变得尤为重要。在这样的背景下,分布式锁的概念应运而生,它为分布式系统提供了一种协调机制,以确保在任何给定时间只有一个进程可以执行特定的操作。而ZooKeeper,作为一个开源的分布式协调服务,因其在分布式锁实现中的重要作用而广受关注。

二、ZooKeeper简介

2.1 ZooKeeper的基本概念和架构

ZooKeeper的核心是一个简单的数据模型,它由一系列被称为ZNode的节点组成,这些节点按照层次结构组织,类似于文件系统的目录结构。每个ZNode可以存储数据和子节点,并且具有版本号,确保了数据的一致性和顺序性。

2.2 ZooKeeper的核心特性

  • 一致性:ZooKeeper保证了客户端将看到同一个视图,即所有客户端对ZooKeeper的请求将按照顺序执行。
  • 可靠性:一旦更新了服务状态,除非再次进行更新,否则状态信息不会改变。
  • 顺序性:客户端的更新将按照发送顺序进行处理。

2.3 ZooKeeper的安装步骤

ZooKeeper的安装相对简单,以下是基本的安装步骤:

  1. 下载ZooKeeper:访问Apache ZooKeeper的官方网站下载最新版本的ZooKeeper。
  2. 解压文件:将下载的压缩包解压到你选择的目录。
  3. 配置ZooKeeper:进入解压后的目录,编辑conf/zoo.cfg配置文件,配置ZooKeeper服务器的详细信息。
  4. 创建ZooKeeper数据目录:在ZooKeeper的安装目录下创建一个名为data的目录,并在其中创建一个名为myid的文件,该文件包含一个数字,代表服务器的唯一标识。
  5. 启动ZooKeeper服务:在命令行中,进入ZooKeeper的bin目录,运行zkServer.sh start命令来启动服务。
  6. 验证服务状态:使用zkServer.sh status命令来检查ZooKeeper服务是否正在运行。
  7. 连接到ZooKeeper:使用命令行客户端zkCli.sh连接到ZooKeeper服务器,进行操作和测试。

2.4 ZooKeeper的一致性保证

ZooKeeper使用ZAB(ZooKeeper Atomic Broadcast)协议来保证数据的一致性。ZAB协议是一种类似于2PC(两阶段提交)的协议,确保了在Leader服务器崩溃的情况下,新的Leader能够被选举出来,并且所有服务器上的数据保持一致。

三、分布式锁的基本概念

3.1 分布式锁的定义

分布式锁是一种在分布式系统中用于确保多个进程对共享资源的互斥访问的机制。在分布式系统中,不同的服务和应用可能需要访问同一资源,而分布式锁确保在任何给定时间只有一个服务或应用能够进行访问,从而避免了资源冲突和数据不一致的问题。

3.2 分布式锁的工作原理

分布式锁的工作原理通常涉及以下几个关键步骤:

  • 锁的申请:当一个服务或应用需要访问共享资源时,它首先向分布式锁服务申请锁。
  • 锁的持有:如果锁是可用的,服务或应用将获得锁,并开始执行其操作。
  • 锁的释放:一旦操作完成,服务或应用必须释放锁,以便其他服务或应用可以访问资源。

3.3 分布式锁的类型

分布式锁可以分为几种类型,包括:

  • 排他锁:也称为独占锁,一次只允许一个服务或应用持有锁。
  • 共享锁:允许多个服务或应用同时持有锁,但每个服务或应用只能读取资源,不能修改。
  • 可重入锁:允许持有锁的服务或应用请求同一锁而不会导致死锁。

3.4 分布式锁的挑战

实现分布式锁面临一些挑战,包括:

  • 性能问题:锁的申请和释放可能会增加系统的延迟。
  • 死锁问题:不当的锁管理可能导致服务或应用永久等待锁的释放。
  • 容错性问题:分布式锁服务需要能够处理节点故障和其他异常情况。

3.5 分布式锁的应用场景

分布式锁在多种场景下都非常有用,例如:

  • 数据库操作:确保对数据库记录的原子操作。
  • 缓存更新:同步对缓存的更新,防止缓存不一致。
  • 任务调度:协调分布式任务的执行,防止任务重复执行。
  • 资源分配:在资源有限的情况下,合理分配资源给不同的服务或应用。

四、ZooKeeper实现分布式锁的原理

4.1 深入理解ZooKeeper的数据模型:ZNode

ZooKeeper的核心是其数据模型,它由一系列的ZNode组成,这些节点构成了一个类似于文件系统的层次结构。每个ZNode可以存储数据,拥有自己的属性,如版本号、权限等,并且可以有子节点。ZNode分为持久节点、临时节点和顺序节点,每种节点类型在分布式锁的实现中扮演不同的角色。

4.2 ZooKeeper的节点创建、删除和监听机制

ZooKeeper提供了一套API,允许客户端创建、查询、更新和删除ZNode。此外,客户端可以对ZNode设置监听器,当ZNode的状态发生变化时,ZooKeeper会通知所有设置了监听的客户端。这一机制是实现分布式锁的关键。

  • 创建节点:客户端可以创建一个新的ZNode,如果指定为临时节点,那么当客户端断开连接时,该节点会被自动删除。
  • 删除节点:客户端可以删除ZNode,这通常用于释放锁。
  • 监听机制:客户端可以监听ZNode的变化,这在分布式锁中用于等待锁的释放。

4.3 临时顺序节点与分布式锁的关系

在ZooKeeper实现分布式锁时,临时顺序节点的使用至关重要。当多个客户端同时尝试创建具有相同路径的临时顺序节点时,ZooKeeper会为这些节点分配一个唯一的序列号。客户端可以根据序列号判断自己是否获得了锁。

  • 序列号:每个顺序节点都会被分配一个全局唯一的序列号。
  • 锁的获取:客户端尝试创建一个临时顺序节点,序列号最小的节点代表获得了锁。
  • 锁的顺序:其他客户端可以根据序列号顺序等待,一旦前面的节点释放锁(即节点被删除),下一个序列号的节点将获得锁。

4.4 分布式锁的安全性保障

ZooKeeper提供了一种机制,确保即使在网络分区或其他异常情况下,分布式锁的安全性也不会受到影响。

  • 领导者选举:在ZooKeeper中,如果Master节点失败,会通过领导者选举机制快速选出新的Master节点。
  • 会话超时:每个客户端与ZooKeeper的会话都有一个超时时间,如果客户端在超时时间内没有与ZooKeeper通信,会话将被关闭,临时节点将被删除,从而释放锁。

4.5 ZooKeeper的Watcher机制

Watcher是ZooKeeper中的一个关键特性,它允许客户端注册对特定ZNode的监听,当ZNode发生变化时,ZooKeeper会通知所有注册了Watcher的客户端。

  • 注册Watcher:客户端在创建或查询ZNode时可以注册Watcher。
  • 事件通知:当ZNode发生变化,如节点被创建或删除,ZooKeeper会向所有注册了Watcher的客户端发送通知。
  • 状态监控:Watcher机制使得客户端能够监控锁的状态,及时响应锁的释放。

4.6 分布式锁的性能考虑

虽然ZooKeeper提供了强大的分布式锁实现机制,但在设计时也需要考虑性能因素。

  • 网络延迟:ZooKeeper的操作依赖于网络通信,网络延迟可能会影响锁的性能。
  • 系统负载:ZooKeeper服务器的负载也会影响锁的性能,需要合理配置和优化。
  • 锁的粒度:锁的粒度越细,系统的性能可能会越高,但也可能导致更多的管理开销。

五、ZooKeeper分布式锁的实现步骤

5.1 创建锁的ZNode

在ZooKeeper中创建分布式锁首先需要定义一个锁的路径。所有需要锁的服务或应用都会尝试在这个路径下创建一个临时顺序节点。

String lockPath = "/distributedLock";

5.2 客户端尝试获取锁的过程

客户端尝试获取锁时,会向ZooKeeper请求创建一个临时顺序节点。如果该节点是路径下的第一个节点,那么客户端获得锁。

CountDownLatch latch = new CountDownLatch(1);
try {// 创建一个临时顺序节点CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181");client.start();String createdNode = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(lockPath + "/node-", "lock-data".getBytes());// 如果是第一个节点,则获得锁List<String> children = client.getChildren().forPath(lockPath);if (children.size() == 1 && children.get(0).equals(createdNode.substring(lockPath.length() + 1))) {System.out.println("Lock acquired");// 执行临界区代码} else {// 等待锁释放latch.await();}
} catch (Exception e) {e.printStackTrace();
} finally {client.close();
}

5.3 锁的释放和重入机制

当客户端完成操作后,需要释放锁。这通常通过删除对应的临时顺序节点来实现。如果客户端需要再次获得锁,可以重新执行获取锁的过程。

// 释放锁
try {// 假设createdNode是我们创建的节点的完整路径String nodeToDelete = createdNode;client.delete().forPath(nodeToDelete);System.out.println("Lock released");
} catch (Exception e) {e.printStackTrace();
}

5.4 处理会话超时

ZooKeeper中的临时节点会在客户端会话超时后自动删除,这意味着如果客户端崩溃,它所持有的锁会被自动释放。

5.5 处理网络分区

在网络分区的情况下,ZooKeeper的领导者选举机制可以确保有一个节点能够继续提供服务。

5.6 优化锁的性能

为了提高性能,可以采取以下措施:

  • 减少锁的粒度:尽量细化锁的范围,只锁定必要的资源。
  • 批量操作:如果可能,将多个操作作为一个事务来执行,减少网络通信的次数。

5.7 避免死锁

为了避免死锁,可以设置超时时间,如果超过这个时间还没有获得锁,客户端可以采取其他措施,比如重试或放弃操作。

// 设置超时时间
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client.start();
try {String lockNode = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).withACL(Ids.OPEN_ACL_UNSAFE).forPath(lockPath, "c".getBytes());// 使用until来设置超时时间String actualLockNode = client.sync().untilConnectedOrTimedOut();if (actualLockNode != null && actualLockNode.equals(lockNode)) {System.out.println("Lock acquired");// 执行临界区代码} else {System.out.println("Could not acquire lock");}
} catch (Exception e) {e.printStackTrace();
} finally {client.close();
}

六、ZooKeeper分布式锁的优势与限制

6.1 优势:性能、可靠性、易用性

6.1.1 性能优势

ZooKeeper分布式锁的性能优势主要体现在其高效的节点创建和删除操作上。由于ZooKeeper内部优化,如利用内存存储数据和高效的数据复制机制,使得锁的获取和释放操作非常快速。

6.1.2 可靠性优势

ZooKeeper的可靠性来自于其设计上的持久性保障。即使在部分节点故障的情况下,ZooKeeper集群依然能够保证服务的可用性,这为分布式锁提供了强大的可靠性支持。

6.1.3 易用性优势

ZooKeeper提供了简单的API和客户端库,使得开发者可以轻松地在自己的应用程序中实现分布式锁。此外,ZooKeeper社区活跃,提供了大量的文档和示例,进一步降低了使用门槛。

6.2 限制:网络分区、性能瓶颈、单点问题

6.2.1 网络分区问题

ZooKeeper分布式锁的一个主要限制是网络分区问题。在网络分区发生时,ZooKeeper集群可能会分裂成两个独立的部分,导致锁服务不可用或产生多个锁持有者。

6.2.2 性能瓶颈问题

尽管ZooKeeper的性能通常很高,但在极端情况下,如高并发的锁请求,ZooKeeper服务器可能会成为性能瓶颈。此外,网络延迟也可能影响锁的性能。

6.2.3 单点问题

虽然ZooKeeper集群设计用于处理节点故障,但在某些配置下,如果Master节点负载过高或故障,可能会影响整个集群的性能和稳定性。

6.3 与CAP定理的关系

6.3.1 一致性和可用性

ZooKeeper遵循CAP定理中的一致性和可用性原则。在网络分区发生时,ZooKeeper倾向于保证一致性,这可能导致在某些情况下牺牲可用性。

6.3.2 分区容错性

ZooKeeper的设计允许在网络分区的情况下继续提供服务,但可能会限制某些操作,以确保数据的一致性。

6.4 与其他分布式锁实现的对比

6.4.1 与Redis分布式锁的对比

Redis分布式锁提供了快速的锁获取和释放操作,但在网络问题或Redis服务故障时,锁可能会丢失,导致可靠性不如ZooKeeper。

6.4.2 与Etcd分布式锁的对比

Etcd是一个基于Raft协议的分布式键值存储,它提供了强一致性的分布式锁实现。与ZooKeeper相比,Etcd在某些场景下可能提供更高的吞吐量,但在易用性方面可能不如ZooKeeper。

6.5 优化和改进策略

6.5.1 集群优化

通过优化ZooKeeper集群的配置,如增加服务器数量、优化网络设置,可以提高其性能和可靠性。

6.5.2 客户端优化

在客户端实现上,可以通过重试机制、超时设置和锁续期策略来优化分布式锁的性能和稳定性。

6.5.3 监控和报警

实施监控和报警机制可以帮助及时发现和解决ZooKeeper集群的问题,从而保证分布式锁服务的稳定性。

七、案例分析

7.1 案例引入

在本章节中,我们将通过一系列实际案例来展示ZooKeeper分布式锁的应用。这些案例将涵盖不同的业务场景,包括但不限于数据库操作、任务调度、资源分配等。

7.2 数据库操作同步

7.2.1 场景描述

在分布式数据库环境中,多个服务实例可能会尝试同时更新相同的记录。使用ZooKeeper分布式锁可以确保同一时间只有一个服务实例能够进行更新。

7.2.2 实现步骤
  • 获取锁:服务实例在执行更新操作前,首先向ZooKeeper请求一个分布式锁。
  • 执行操作:获得锁的服务实例执行数据库更新操作。
  • 释放锁:操作完成后,服务实例释放锁,允许其他服务实例进行更新。
7.2.3 代码示例
// 伪代码,展示获取和释放锁的过程
String lockPath = "/lock/dbUpdate";
try {// 尝试获取锁boolean hasLock = zkClient.createLock(lockPath);if (hasLock) {// 安全地执行数据库操作updateDatabase();// 释放锁zkClient.releaseLock(lockPath);}
} catch (Exception e) {// 处理异常
}

7.3 分布式任务调度

7.3.1 场景描述

在分布式任务调度系统中,需要确保任务不会重复执行。通过ZooKeeper分布式锁,可以控制任务的并发执行。

7.3.2 实现步骤
  • 任务注册:任务在调度前在ZooKeeper上注册一个临时节点。
  • 任务执行:持有锁的任务节点执行任务。
  • 任务完成:任务完成后,节点被删除,锁被释放。
7.3.3 代码示例
// 伪代码,展示任务调度和锁的管理
String taskLockPath = "/taskLock";
try {// 创建临时顺序节点String taskNode = zkClient.createEphemeralSequential(taskLockPath, "taskData");// 判断是否为最小序号,即是否获得执行权if (isMinimumNode(taskNode, taskLockPath)) {// 执行任务executeTask();}
} catch (Exception e) {// 处理异常
}

7.4 资源分配

7.4.1 场景描述

在资源受限的情况下,如计算资源或I/O资源,需要合理分配以避免冲突和竞争。

7.4.2 实现步骤
  • 资源请求:服务实例请求资源时,向ZooKeeper发送请求。
  • 资源锁定:ZooKeeper为请求的服务实例分配一个锁。
  • 资源使用:服务实例在获得锁后使用资源。
  • 资源释放:使用完毕后释放资源和锁。
7.4.3 代码示例
// 伪代码,展示资源分配和锁的管理
String resourceLockPath = "/resourceLock";
try {// 获取资源锁boolean lockAcquired = zkClient.acquireLock(resourceLockPath);if (lockAcquired) {// 使用资源utilizeResource();// 释放锁zkClient.releaseLock(resourceLockPath);}
} catch (Exception e) {// 处理异常
}

7.5 避免死锁

7.5.1 场景描述

在复杂的系统中,不当的锁管理可能导致死锁。

7.5.2 解决方案
  • 超时机制:为锁的获取设置超时时间。
  • 锁续期:定期续期持有的锁,防止因长时间持有而造成死锁。
  • 死锁检测:实现死锁检测机制,及时释放不必要的锁。
7.5.3 代码示例
// 伪代码,展示死锁预防措施
String lockPath = "/preventDeadlock";
try {// 设置锁获取超时时间boolean hasLock = zkClient.tryLock(lockPath, timeout);if (hasLock) {// 执行操作performOperation();// 定期续期锁zkClient.renewLock();} else {// 处理超时情况handleTimeout();}
} catch (Exception e) {// 处理异常
}

八、与其他分布式锁实现的比较

8.1 引言

在选择分布式锁的实现方案时,了解不同技术的优势和劣势是至关重要的。本章节将对ZooKeeper分布式锁与其他流行的分布式锁实现进行比较,包括Redis、Etcd以及Apache Helix等,以帮助开发者做出更明智的技术选择。

8.2 ZooKeeper vs Redis

8.2.1 Redis的优势
  • 性能:Redis作为一个内存数据存储,其读写速度非常快。
  • 简单性:Redis的使用和部署相对简单,社区支持强大。
8.2.2 Redis的限制
  • 数据持久性:虽然Redis支持数据持久化,但在某些配置下,数据丢失的风险仍然存在。
  • 单点故障:在没有使用Redis Sentinel或Cluster的情况下,Redis可能面临单点故障问题。
8.2.3 ZooKeeper的优势
  • 可靠性:ZooKeeper的集群模式提供了高可靠性和容错性。
  • 顺序性保证:ZooKeeper能够保证操作的顺序性,这对于某些应用场景非常关键。
8.2.4 ZooKeeper的限制
  • 性能:与Redis相比,ZooKeeper的性能可能略低,尤其是在高负载情况下。
  • 复杂性:ZooKeeper的学习曲线和部署复杂性相对较高。

8.3 ZooKeeper vs Etcd

8.3.1 Etcd的优势
  • 强一致性:基于Raft协议,Etcd提供了强一致性的保证。
  • 高可用性:Etcd设计为高可用性,能够在节点故障时继续运行。
8.3.2 Etcd的限制
  • 性能:虽然Etcd的性能优异,但在某些高并发场景下可能需要更多的调优。
  • 资源消耗:Etcd可能需要更多的资源来维持其高可用性和一致性。
8.3.3 ZooKeeper的优势
  • 成熟度:ZooKeeper作为一个成熟的技术,拥有广泛的用户基础和丰富的实践经验。
  • 社区和生态:ZooKeeper背后有一个强大的社区和生态系统,提供了大量的工具和集成。
8.3.4 ZooKeeper的限制
  • 一致性模型:与Etcd的强一致性相比,ZooKeeper的一致性模型可能在某些场景下不够强大。

8.4 ZooKeeper vs Apache Helix

8.4.1 Apache Helix的优势
  • 为分布式锁设计:Helix是专门为管理分布式锁和集群状态而设计的。
  • 细粒度控制:Helix提供了对分布式锁的细粒度控制。
8.4.2 Apache Helix的限制
  • 复杂性:Helix的架构相对复杂,需要更多的配置和管理。
  • 资源需求:Helix可能需要更多的计算资源来维护其状态和管理。
8.4.3 ZooKeeper的优势
  • 简洁性:ZooKeeper提供了一个简洁的API,易于集成和使用。
  • 广泛的应用:ZooKeeper不仅用于分布式锁,还可以用于配置管理、服务发现等多种场景。
8.4.4 ZooKeeper的限制
  • 特定场景的适用性:在一些需要高度专业化的分布式锁管理的场景中,ZooKeeper可能不如Helix适用。

8.5 选择适合的分布式锁实现

在选择分布式锁实现时,需要考虑以下因素:

  • 应用场景:不同的技术可能更适合特定的应用场景。
  • 性能需求:根据系统的性能需求选择合适的技术。
  • 可靠性和一致性:根据系统对可靠性和一致性的要求进行选择。
  • 资源和维护成本:考虑技术实施的资源和维护成本。

8.6 结论

每种分布式锁实现都有其独特的优势和限制。ZooKeeper以其可靠性、顺序性保证和成熟度在分布式锁领域占有一席之地。然而,根据具体的应用需求和场景,Redis、Etcd和Apache Helix等其他技术可能提供更适合的解决方案。理解每种技术的优缺点,并结合实际需求进行选择,是构建高效、可靠分布式系统的关键。

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

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

相关文章

Vapor Mode:Vue.js 的速度与激情,代码界的闪电侠

大家好&#xff0c;我是宝哥。 在快速发展的网络开发世界中&#xff0c;创新的Vue.js团队给我们带来了Vapor Mode。这个新模式优化了Vue的核心渲染过程&#xff0c;帮助我们的应用程序像轻烟一样运行&#xff0c;开发者无需深入复杂的优化工作。 在这篇文章中&#xff0c;我们将…

硝基酪氨酸检测试剂盒Nitrotyrosine ELISA Kit,比色法

硝基酪氨酸是反应体内氧化应激的标记物质。StressMarq可以提供硝基络氨酸的检测试剂盒。酪氨酸硝基化会导致细胞功能损伤。因此检测样本中的硝基酪氨酸含量尤为重要。StressMarq的硝基络氨酸检测试剂盒有多篇文献引用&#xff0c;产品质量有保障。除了有硝基络氨酸的检测试剂盒…

【微积分】Grant Sanderson

梯度&#xff1a;将各个偏导打包 定义&#xff1a;direction of steepest ascent 梯度向量的长度&#xff1a;最速上升方向的陡峭程度 方向导数&#xff1a;偏导的一种拓展 【托马斯微积分学习日记】13.1-线积分_哔哩哔哩_bilibili 概述 16.1line integrals of scalar funct…

直播预告:TinyVue 组件库实战解析,提升组件库构建技能!

在复杂的编码世界里&#xff0c;大家总希望能够寻找更高效、更简洁的解决方案来优化工作流程&#xff0c;提升开发效率。在5月28日晚7点 OpenTiny B站直播间&#xff0c;OpenTiny 非常荣幸地为大家带来一场关于 TinyVue 组件库实战分享的直播。届时&#xff0c;TinyVue 组件库成…

openEuler系统通过shell脚本安装openGauss 5.0.0企业版

上次提到的开机自启动的配置&#xff0c;获得了LD的称赞&#xff0c;然而LD的要求&#xff0c;都是“既得陇复望蜀”的&#xff0c;他又期望我们能实现openGauss安装的“自动化”&#xff0c;于是尝试了下用shell脚本部署&#xff0c;附件中的脚本实测有效&#xff0c;openEule…

JDBC的 PreparedStatement 的用法和解释

文章目录 前言1、封装数据库连接和关闭操作数据库配置文件 config.properties 2、批量添加操作3、查询操作4、修改和删除操作总结 前言 PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程 1、封装数据库连接和关闭操作 package org.springblade.m…

Leetcode 力扣90. 子集 II (抖音号:708231408)

给你一个整数数组 nums &#xff0c;其中可能包含重复元素&#xff0c;请你返回该数组所有可能的 子集 &#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。返回的解集中&#xff0c;子集可以按 任意顺序 排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,2…

线程安全-3 JMM

一.谈一下JMM 1.JMM&#xff0c;JavaMemoryModel&#xff0c;Java内存模型。定义了多线程对共享内存读写操作的行为规范&#xff0c;通过规范多线程对共享内存的读写操作&#xff0c;以保证指令执行和结果的正确性。 2.JMM把内存分为两块 &#xff08;1&#xff09;主内存&a…

图论(二)-图的建立

引言&#xff1a; 建图&#xff0c;将图放进内存的方法 常用的建图方式&#xff1a;邻接矩阵&#xff0c;邻接链表&#xff0c;链式前向星 一、邻接矩阵 通过一个二维数组即可将图建立&#xff0c;邻接矩阵&#xff0c;考虑节点集合 &#xff0c;用一个二维数组定义邻接矩…

迭代器模式(行为型)

目录 一、前言 二、迭代器模式 三、总结 一、前言 迭代器模式(Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;提供一种方法顺序访问一个聚合对象中各个元素&#xff0c;而又不暴露该对象的内部表示。总的来说就是分离了集合对象的遍历行为&#xff0c;抽象出…

gcc -m32 一堆报错:No such file or directory

问题&#xff1a;终端执行命令gcc -m32 *.c -o xxx.so 出现一堆缺失文件的报错。 解决办法&#xff1a;库没装全&#xff0c;终端执行下面的命令。 sudo apt install gcc-multilib

sql去除表中某个字段的空格

比如表中名字字段&#xff0c;名字之间有空格&#xff0c;需要把空格去除&#xff0c;应该如何写SQL语句实现&#xff1f; 在SQL中&#xff0c;可以使用TRIM函数来去除字符串两端的空格&#xff0c;如果需要去除字段中的所有空格&#xff0c;可以使用REPLACE函数结合TRIM函数。…

【设计模式】JAVA Design Patterns——Command(事务模式)

&#x1f50d;目的 将请求封装为对象&#xff0c;从而使你可以将具有不同请求的客户端参数化&#xff0c;队列或记录请求&#xff0c;并且支持可撤销操作。 &#x1f50d;解释 真实世界例子 有一个巫师在地精上施放咒语。咒语在地精上一一执行。第一个咒语使地精缩小&#xff0…

LabVIEW步开发进电机的串口控制程序

LabVIEW步开发进电机的串口控制程序 为了提高电机控制的精确度和自动化程度&#xff0c;开发一种基于LabVIEW的实时、自动化电机串口控制程序。利用LabVIEW软件的图形化编程特性&#xff0c;通过串口实时控制电机的运行参数&#xff0c;实现电机性能的精准控制与评估。 系统组…

Borel-Cantelli 引理

翻译自大佬 https://huarui1998.com/Notes/math/borel-cantelli.html 1. 集序列的 lim ⁡ inf ⁡ \lim\inf liminf 和 lim ⁡ sup ⁡ \lim\sup limsup 类似于定义实数序列 { a k } \{a_k\} {ak​} 的 lim ⁡ inf ⁡ \lim\inf liminf 和 lim ⁡ sup ⁡ \lim\sup limsup, …

nginx+nginx-http-flv-module在Linux服务器搭建

需求 在服务器搭建点播/视频平台的话需要在服务器搭建nginx和rtmp模块 rtmp模块 rtmp 模块有 nginx-rtmp-module &#xff0c;但是我们这里使用 nginx-http-flv-module 来替代。因为后者是基于前者开发的&#xff0c;前者拥有的功能后者都有&#xff0c;后者是国内的开发开…

基于PostGIS的mvt动态矢量切片的后台地图服务和前端调用

目录 一、背景 二、矢量切片 三、Mapbox的矢量切片格式 四、PostGIS生成矢量切片 ST_AsMVT: ST_AsMVTGeom: 五、导入试验数据 六、编写PostGIS函数 七:Java后端实现 八、Openlayers前端调用 一、背景 矢量切片技术目前已成为互联网地图的主流技术,无论是Mapbox还…

Vue.Draggable:强大的Vue拖放组件技术探索

一、引言 随着前端技术的不断发展&#xff0c;拖放&#xff08;Drag-and-Drop&#xff09;功能已经成为许多Web应用不可或缺的一部分。Vue.js作为现代前端框架的佼佼者&#xff0c;为开发者提供了丰富的生态系统和强大的工具链。Vue.Draggable作为基于Sortable.js的Vue拖放组件…

星网智云总经理韦炜:低代码与智能制造融合,探索未来制造的无限可能

下文为广西星网智云总经理韦炜的演讲全文&#xff1a; 大家下午好&#xff0c;今天给大家分享一下玉柴的数字化转型过程&#xff0c;以及整个过程中我们会怎样使用低代码。 ﹀ ﹀ ﹀ #玉柴数字化战略 #两个要点 我们的数字化&#xff0c;是在公司的整一个战略转型下去做的。…

Slash后台管理系统源码阅读笔记 后面面板中的折线图统计卡片是怎么实现的?

之前的笔记发表在博客和公众号以后&#xff0c;得到了一部分同学的喜爱的认可&#xff0c;所以今天继续。 目前这个管理系统的代码已经处理了一小部分&#xff1a; 接下来&#xff0c;我们看看第二栏那三个折线图统计卡片是怎么实现的。 这三个卡片还是使用的 antd 一行三列…