分布式1(cap base理论 锁 事务 幂等性 rpc)
目录
分布式系统介绍
一、定义与概念
二、分布式系统的特点
三、分布式系统面临的挑战
四、分布式系统的常见应用场景
CAP 定理
BASE 理论
BASE理论是如何保证最终一致性的
分布式锁的常见使用场景有哪些?
1. 防止多节点重复操作
2. 资源互斥访问
3. 分布式事务
4. 全局 ID 生成器
5. 分布式限流
6. 任务调度与协调
7. 防止缓存雪崩
8. 分布式系统初始化
常见实现方式
避免分布式锁带来的死锁问题?
1. 设置锁超时时间
2. 按顺序获取锁
3. 使用可重入锁
4. 实现锁的公平性
5. 检测与恢复机制
6. 原子化操作
7. 锁粒度控制
8. 心跳机制与健康检查
9. 事务与补偿机制
10. 降级与熔断策略
分布式环境下高可用解决方案
分布式事务
一、分布式事务的挑战
二、常见解决方案
1. 两阶段提交(2PC)
2. 三阶段提交(3PC)
3. TCC(Try-Confirm-Cancel)
4. 消息队列(最终一致性)
5. Saga 模式
6. 最大努力通知
三、分布式事务工具
四、最佳实践
五、对比总结
接口的幂等性/解决重复消费
接口幂等性
解决重复消费
分布式唯一ID
概述
常见实现方案
1. UUID (Universally Unique Identifier)
2. 数据库自增 ID
3. Snowflake 算法(雪花算法)
4. Redis 原子操作
5. 数据库号段模式
6. ULID (Universally Unique Lexicographically Sortable Identifier)
选择建议
RPC
一、核心概念
二、常见 RPC 框架
三、工作流程
四、核心技术
五、优缺点
六、Java 实现示例(gRPC)
七、适用场景
八、对比 REST API
Protocol Buffers vs Thrift
一、核心特性对比
二、序列化性能
三、IDL(接口定义语言)
Protobuf 示例
Thrift 示例
四、RPC 集成
五、适用场景
六、优缺点总结
Protocol Buffers
Thrift
七、选择建议
八、性能对比测试
九、总结
分布式系统介绍
一、定义与概念
分布式系统是由多个通过网络连接的独立计算机节点组成的系统,这些节点相互协作,共同完成一个或多个任务,对外表现得像一个单一的系统。每个节点都有自己的处理器、内存和存储,它们通过消息传递进行通信和协调。例如,大型电商平台如淘宝、京东,背后支撑其运行的就是复杂的分布式系统,众多服务器节点分别负责用户管理、商品展示、订单处理等不同功能,协同为用户提供服务。
二、分布式系统的特点
- 分布性:组件分布在不同的物理或虚拟节点上,这些节点可能位于不同地理位置的数据中心。例如,一家跨国公司的业务系统,其用户数据可能存储在位于不同国家的数据中心节点上。
- 并发性:多个节点可能同时处理不同的任务,节点之间需要协调和同步操作,以避免数据冲突等问题。比如在分布式数据库中,多个节点可能同时对相同的数据进行读写操作。
- 故障独立性:某个节点出现故障不应导致整个系统崩溃,其他节点应能继续提供部分或全部功能。像云存储系统,即使个别存储节点发生故障,用户仍然可以访问和存储数据。
- 透明性:对于用户和应用程序而言,分布式系统的复杂性被隐藏,它们看到的就像一个单一的系统。例如,用户在使用在线支付功能时,无需关心背后是由多少个分布式节点协同完成支付处理的。
三、分布式系统面临的挑战
- 网络问题
- 网络延迟:不同节点间的数据传输需要时间,可能导致操作响应缓慢。比如,从欧洲的数据中心向亚洲的数据中心请求数据,由于物理距离远,网络延迟较高。
- 网络分区:网络故障可能导致部分节点间通信中断,形成网络分区,使系统出现数据不一致等问题。
- 数据一致性
在分布式环境下,确保多个节点上的数据一致性是一大挑战。例如,在分布式数据库的读写操作中,不同节点可能由于网络延迟等原因,在同一时刻的数据状态不一致。 - 故障处理
由于节点众多,某个节点发生故障的可能性增加。需要设计有效的故障检测、恢复和容错机制,以保证系统的可用性。例如,服务器硬件故障、软件崩溃等情况都需要系统能够快速应对。
四、分布式系统的常见应用场景
- 大型网站和互联网应用:如社交媒体平台、在线游戏平台等,需要处理海量用户的并发请求,通过分布式系统可以实现水平扩展,提高系统的处理能力和可用性。
- 大数据处理:分布式计算框架如 Hadoop、Spark 用于处理大规模数据集,将数据和计算任务分布到多个节点上并行处理,加快数据处理速度。
- 云计算平台:提供弹性计算、存储和网络等服务,背后依靠分布式系统实现资源的动态分配和管理,满足不同用户的需求。
分布式系统一定是由多个节点组成的系统。 其中节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的。
微服务架构是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行在自己进程中,服务间通信机制采用轻量级通讯机制(通常是基于 HTTP 的 RESTful API ) 。
CAP 定理
- 定义:CAP 定理指出,在一个分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)这三个特性不能同时满足,最多只能同时满足其中两个。
- 一致性:所有节点在同一时间具有相同的数据,即更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致。
- 可用性:系统在任何时候都能响应客户端的请求,即每个请求都能在有限时间内得到响应,不会出现无限期的等待。
- 分区容错性:系统能够在网络分区的情况下继续正常运行,即当网络出现分区(部分节点之间无法通信)时,系统仍然能够提供服务。
BASE 理论
- 定义:BASE 理论是对 CAP 定理中一致性和可用性权衡的结果,它的核心思想是通过牺牲强一致性来获得高可用性。BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。
- 基本可用:系统在出现故障时,允许损失部分可用性,但仍然能够提供基本的服务。例如,在某些情况下,系统可能会降低响应速度或返回部分数据,但不会完全不可用。
- 软状态:系统中的数据可以存在中间状态,并且允许这种中间状态在一段时间内存在。也就是说,数据不需要在所有节点上立即保持一致,而是可以在一段时间内逐渐达到一致。
- 最终一致性:尽管系统中的数据在一段时间内可能不一致,但最终会达到一致状态。在最终一致性的系统中,只要客户端在一段时间后再次请求数据,就能够得到最新的、一致的数据。
CAP 定理强调了分布式系统中三个关键特性之间的权衡关系,而 BASE 理论则提供了一种在实际应用中实现分布式系统的思路,即在满足分区容错性的前提下,通过牺牲强一致性来换取高可用性和可扩展性。
BASE理论是如何保证最终一致性的
BASE 理论通过以下几种方式来保证最终一致性:
- 异步处理与补偿机制
- 操作异步执行:在分布式系统中,当一个写操作发生时,系统不会立即要求所有相关节点都完成数据更新,而是先将操作记录下来,并异步地将更新请求发送到其他节点。这样可以避免因等待所有节点同步完成而导致的性能下降和可用性降低。
- 补偿操作:如果在异步更新过程中出现错误或失败,系统会采用补偿机制来确保数据最终达到一致。例如,通过重试机制重新发送更新请求,或者执行一些补偿性的操作来修正数据。
- 数据复制与传播
- 多副本数据存储:在分布式系统中,数据通常会在多个节点上进行复制,以提高系统的可用性和容错能力。当一个节点的数据发生更新时,更新会逐渐传播到其他副本节点。
- 传播策略:通过采用合适的传播策略,如基于时间戳或版本号的更新传播方式,确保数据在不同节点之间能够正确地同步。例如,当一个节点接收到一个更新请求时,它会比较请求中的数据版本号与本地数据的版本号,如果请求的版本号更高,则更新本地数据。
- 一致性检查与修复
- 定期检查:系统会定期对数据进行一致性检查,通过比较不同节点上的数据版本、校验和或其他一致性指标,来发现数据不一致的情况。
- 主动修复:一旦发现数据不一致,系统会采取主动修复措施,例如从其他正确的节点获取最新的数据来更新不一致的节点,或者通过一些特定的算法来计算出正确的数据状态并进行修复。
- 事务处理与协调
- 柔性事务:在 BASE 理论中,通常采用柔性事务来处理分布式数据的更新。柔性事务允许在一定程度上放松对事务的严格一致性要求,通过使用一些补偿性的操作来保证最终一致性。例如,使用 TCC(Try - Confirm - Cancel)模式,先尝试执行操作,然后根据情况确认或取消操作,如果出现问题则通过补偿操作来恢复数据的一致性。
- 分布式事务协调:通过分布式事务协调器来管理和协调不同节点之间的事务处理。协调器会跟踪事务的执行状态,并在必要时采取措施来保证事务的最终一致性,如在部分节点出现故障时,协调其他节点进行相应的处理,以确保整个事务能够正确完成。
分布式锁的常见使用场景有哪些?
分布式锁在分布式系统中用于控制多个节点对共享资源的并发访问,常见使用场景包括:
1. 防止多节点重复操作
- 定时任务幂等性:在分布式系统中,多个节点可能同时触发同一个定时任务(如数据同步、报表生成),使用分布式锁可以确保只有一个节点执行任务,避免重复计算或数据冲突。
- 防止重复提交:在高并发场景下,用户可能多次提交同一请求(如支付、订单创建),通过分布式锁可以确保同一操作只被执行一次。
2. 资源互斥访问
- 分布式缓存更新:当多个节点同时更新同一个缓存项时,使用分布式锁可以保证只有一个节点进行更新操作,避免缓存击穿或数据不一致。
- 数据库写冲突:多个节点同时修改同一数据库记录时,通过分布式锁可以防止脏写或数据冲突。例如,库存扣减、账户余额更新等操作。
3. 分布式事务
- 跨服务资源锁定:在分布式事务中,涉及多个服务对不同资源的操作,使用分布式锁可以确保这些操作的原子性。例如,在订单支付过程中,锁定库存和账户余额,防止超卖或余额不足。
4. 全局 ID 生成器
- 唯一 ID 生成:多个节点需要生成全局唯一 ID 时,通过分布式锁可以保证生成的 ID 不重复。例如,使用数据库自增 ID 或 UUID 时,可能存在并发冲突,分布式锁可以确保 ID 生成的唯一性。
5. 分布式限流
- 全局流量控制:在分布式系统中,为防止某个服务被过多请求压垮,可以使用分布式锁实现全局限流。例如,限制同一时间内最多有 N 个请求访问某个资源。
6. 任务调度与协调
- Leader 选举:在分布式集群中,多个节点需要选举一个 Leader 来协调任务分配,使用分布式锁可以确保只有一个节点被选为 Leader。
- 资源抢占:多个节点竞争同一资源(如分布式文件系统中的文件锁、数据库连接池中的连接)时,分布式锁可以实现公平或非公平的资源分配。
7. 防止缓存雪崩
- 热点缓存重建:当缓存失效时,多个节点可能同时尝试重建缓存,导致数据库压力骤增。使用分布式锁可以确保只有一个节点负责重建缓存,其他节点等待缓存更新完成后直接使用新缓存。
8. 分布式系统初始化
- 系统启动互斥:多个节点同时启动时,可能需要对共享资源进行初始化(如配置加载、数据预热),使用分布式锁可以确保初始化操作只被执行一次。
常见实现方式
- 基于数据库:通过数据库的唯一索引或行锁实现。
- 基于 Redis:使用
SETNX
命令或 RedLock 算法。 - 基于 ZooKeeper:通过创建临时有序节点实现公平锁。
- 基于 Etcd:利用其 CAS(Compare - And - Swap)机制实现分布式锁。
选择合适的分布式锁实现需要根据具体场景权衡性能、可靠性和可用性。
避免分布式锁带来的死锁问题?
在分布式系统中,死锁是使用分布式锁时需要特别注意的问题,以下是避免死锁的几种常见方法:
1. 设置锁超时时间
- 自动释放机制:为每个锁设置合理的超时时间(TTL),即使持有锁的节点崩溃或处理超时,锁也会在超时后自动释放,避免其他节点无限等待。
- 示例:在 Redis 中使用
SET key value NX PX timeout
命令,或在 ZooKeeper 中创建临时节点。
2. 按顺序获取锁
- 固定加锁顺序:当需要获取多个锁时,所有节点必须按照相同的顺序获取锁,避免循环等待。例如,若业务需要同时锁定资源 A 和 B,所有节点必须先锁 A 再锁 B。
3. 使用可重入锁
- 避免自我死锁:允许同一节点在持有锁的情况下再次获取锁(需记录重入次数),防止自身因重复请求锁而导致死锁。
4. 实现锁的公平性
- 公平锁机制:使用公平锁(如 ZooKeeper 的临时有序节点)确保锁的获取按照请求顺序进行,避免饥饿和死锁。
5. 检测与恢复机制
- 死锁检测算法:实现分布式死锁检测,定期检查是否存在循环等待的锁依赖关系,一旦发现则强制释放某些锁。
- 超时重试策略:当获取锁失败时,设置合理的重试次数和退避策略(如指数退避),避免频繁重试加剧死锁风险。
6. 原子化操作
- 使用原子指令:利用 Redis 的
SETNX
、RedLock
算法或 ZooKeeper 的 CAS(Compare - And - Swap)操作确保锁的获取和释放是原子性的,减少中间状态导致的死锁。
7. 锁粒度控制
- 细化锁粒度:尽量缩小锁的范围,只锁定关键资源,减少锁的持有时间,从而降低死锁概率。例如,使用分段锁替代全局锁。
8. 心跳机制与健康检查
- 节点状态监控:通过心跳机制检测持有锁的节点是否存活,若节点失效则自动释放锁。例如,在 Redis 中使用 Lua 脚本原子化检查锁和节点状态。
9. 事务与补偿机制
- 柔性事务:对于复杂操作,使用 TCC(Try - Confirm - Cancel)或 Saga 模式替代传统的刚性事务,通过补偿操作释放已获取的锁。
10. 降级与熔断策略
- 服务保护:当系统负载过高或锁竞争过于激烈时,自动降级某些非关键业务,避免因锁等待导致的级联故障。
分布式环境下高可用解决方案
在分布式环境下,实现高可用的解决方案通常涉及到多个方面,包括冗余设计、故障检测与恢复、负载均衡、数据一致性等。以下是一些常见的分布式环境下高可用解决方案:
- 冗余与集群技术
- 服务器冗余:部署多个服务器节点,通过集群技术将它们组成一个逻辑整体。当某个节点出现故障时,其他节点可以接管其工作,实现自动故障转移,如通过 Keepalived 实现服务器的主备切换。
- 数据冗余:采用数据复制技术,将数据同步到多个节点或不同的数据中心,确保数据的安全性和可访问性。如 MySQL 的主从复制,以及分布式文件系统 Ceph 通过多副本机制实现数据冗余。
- 故障检测与恢复
- 健康检查机制:通过心跳检测、监控系统等手段,定期检查节点和服务的状态。如 ZooKeeper 通过心跳机制检测节点的存活状态,发现故障节点后通知其他节点进行相应处理。
- 自动恢复策略:一旦检测到故障,系统自动触发恢复流程,如自动重启故障服务、切换到备用节点或进行数据恢复操作。像 Kubernetes 可以自动重启故障的容器,并根据预设的策略重新调度到其他可用节点上。
- 负载均衡
- 请求分发:使用负载均衡器将客户端请求均匀分配到多个服务器节点上,避免单点负载过高。常见的负载均衡器有 Nginx、F5 等,可以根据不同的算法(如轮询、加权轮询、最少连接数等)进行请求分发。
- 动态负载调整:根据服务器的负载情况实时调整负载均衡策略,将请求分配到负载较轻的节点上。一些云平台提供的负载均衡服务可以自动根据服务器的 CPU、内存等资源使用情况进行动态调整。
- 数据一致性保证
- 分布式一致性算法:采用如 Paxos、Raft 等一致性算法,确保在分布式环境中数据的一致性。这些算法通过选举领导者、日志复制等方式,保证数据在多个节点之间的一致状态。
- 数据同步机制:对于分布式数据库或存储系统,需要建立可靠的数据同步机制,确保数据在不同节点之间的及时更新和一致。如 Cassandra 等分布式数据库通过 gossip 协议进行数据同步和状态传播。
- 分布式配置管理
- 集中式配置管理:使用集中式的配置管理工具(如 Apollo、Nacos),将系统的配置信息统一管理和分发。这样可以方便在分布式环境中对各个节点的配置进行修改和更新,确保配置的一致性和准确性。
- 配置版本控制:对配置信息进行版本管理,记录配置的变更历史,方便回滚和跟踪。同时,支持配置的动态更新,使系统能够在不重启的情况下应用新的配置。
- 监控与告警系统
- 全面的监控体系:建立覆盖整个分布式系统的监控系统,包括服务器性能、网络状况、应用程序指标等方面的监控。通过收集和分析这些监控数据,及时发现潜在的问题和异常。如 Prometheus 可以收集各种指标数据,并通过 Grafana 进行可视化展示。
- 实时告警机制:当监控到异常情况时,能够及时发送告警信息给相关人员。告警方式可以包括邮件、短信、即时通讯等多种方式,以便快速响应和处理问题。
- 服务治理
- 服务注册与发现:使用服务注册中心(如 Eureka、Consul),让服务提供者将自己的服务信息注册到中心,服务消费者可以从中心获取服务的地址和相关信息,实现服务的动态发现和调用。
- 熔断与降级:当某个服务出现故障或负载过高时,通过熔断机制快速切断对该服务的调用,避免故障扩散。同时,根据业务情况进行服务降级,如返回缓存数据或简化的响应结果,保证系统的核心功能仍然可用。
这些解决方案通常需要结合具体的业务场景和技术架构进行综合应用,以构建一个高可用的分布式系统。
分布式事务
分布式事务是指操作跨越多个资源(如数据库、服务)的事务,需要保证这些操作的原子性、一致性、隔离性和持久性(ACID)。在分布式系统中,由于网络延迟、节点故障等因素,实现分布式事务具有挑战性。以下是常见的解决方案和关键概念:
一、分布式事务的挑战
- CAP 定理:在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)只能同时满足两个。
- BASE 理论:通过牺牲强一致性换取高可用性,采用最终一致性模型。
- 网络问题:跨节点通信可能失败或延迟,导致数据不一致。
二、常见解决方案
1. 两阶段提交(2PC)
- 流程:
- 准备阶段:协调者向所有参与者发送事务请求,参与者执行操作并反馈结果。
- 提交阶段:若所有参与者成功,则协调者发送提交指令;否则回滚。
- 优点:实现强一致性。
- 缺点:同步阻塞,单点故障(协调者),性能差。
- 适用场景:对一致性要求极高且节点数少的场景。
2. 三阶段提交(3PC)
- 改进:在 2PC 基础上增加CanCommit阶段,减少参与者阻塞时间,并引入超时机制。
- 流程:CanCommit → PreCommit → DoCommit。
- 优点:减少阻塞,提高可用性。
- 缺点:仍存在数据不一致风险(如网络分区)。
3. TCC(Try-Confirm-Cancel)
- 柔性事务:将事务分为三个阶段:
- Try:预留资源(如冻结账户余额)。
- Confirm:确认执行,提交资源。
- Cancel:回滚,释放预留资源。
- 优点:无长时间锁,性能高。
- 缺点:开发成本高,需要编写补偿逻辑。
- 示例:支付系统中,先冻结用户余额(Try),订单确认后扣款(Confirm),失败则解冻(Cancel)。
4. 消息队列(最终一致性)
- 流程:
- 业务操作完成后发送消息到队列。
- 消费者异步处理消息,更新相关资源。
- 通过重试或补偿机制确保最终一致性。
- 优点:高吞吐量,解耦服务。
- 缺点:不保证实时一致性,需幂等设计。
- 工具:RocketMQ、Kafka 等支持事务消息的队列。
5. Saga 模式
- 长事务分解:将大事务拆分为多个本地事务,每个子事务有对应的补偿操作。
- 两种模式:
- 向前恢复:失败时重试子事务。
- 向后恢复:失败时执行补偿操作回滚。
- 优点:无单点问题,适合长流程事务。
- 缺点:补偿逻辑复杂,不保证强一致性。
6. 最大努力通知
- 流程:
- 业务操作完成后发送通知(如短信、邮件)。
- 接收方定期查询状态,确认最终结果。
- 适用场景:对实时性要求不高的场景(如支付结果通知)。
三、分布式事务工具
- Seata:阿里巴巴开源框架,支持 AT(自动补偿)、TCC、Saga 等模式。
- ByteTCC:支持 TCC 和消息事务。
- OpenTransaction:轻量级分布式事务框架。
- 数据库原生支持:如 MySQL XA 协议、PostgreSQL 的 dblink。
四、最佳实践
- 优先避免分布式事务:通过业务设计减少跨服务事务(如缓存、最终一致性)。
- 选择合适的一致性级别:根据业务场景权衡强一致性和可用性(如支付用 2PC,订单用最终一致性)。
- 幂等设计:所有事务操作必须支持重试(如唯一 ID 防重复提交)。
- 监控与补偿:建立事务监控系统,对失败事务自动重试或人工干预。
- 熔断与降级:对关键服务设置熔断机制,防止级联故障。
五、对比总结
方案 | 一致性级别 | 性能 | 实现复杂度 | 适用场景 |
---|---|---|---|---|
2PC | 强一致 | 低 | 低 | 金融转账等严格场景 |
TCC | 最终一致 | 中 | 高 | 业务流程长且需回滚 |
消息队列 | 最终一致 | 高 | 中 | 异步通知、高并发场景 |
Saga | 最终一致 | 高 | 高 | 微服务长事务 |
最大努力通知 | 最终一致 | 极高 | 低 | 实时性要求不高的通知 |
根据业务需求选择合适的方案,通常结合多种模式以达到最佳效果。
接口的幂等性/解决重复消费
在分布式系统中,接口的幂等性和解决重复消费是非常重要的概念,它们有助于确保系统的稳定性和数据的一致性。以下是关于它们的详细介绍:
接口幂等性
- 定义:幂等性是指一个接口在多次调用时,产生的结果与一次调用相同,不会对系统造成额外的副作用。例如,对于查询接口,无论调用多少次,只要查询条件不变,返回的结果应该是一致的;对于删除接口,多次删除同一个资源,结果与一次删除相同,即资源被删除,不会出现多次删除导致错误的情况。
- 实现方式
- 使用唯一 ID:在请求中携带一个唯一的标识,如订单号、交易 ID 等。服务端在处理请求时,根据这个唯一 ID 判断请求是否已经被处理过。如果已经处理过,则直接返回之前的结果,不再执行实际的业务逻辑。
- 数据库约束:利用数据库的唯一约束来保证幂等性。例如,在插入数据时,通过设置唯一索引,如果重复插入相同的数据,数据库会抛出异常,从而避免数据重复插入。
- 状态机控制:对于一些有明确状态流转的业务,如订单的创建、支付、发货等,可以通过状态机来控制接口的幂等性。只有当订单处于特定状态时,才能执行相应的操作,避免重复操作导致状态混乱。
解决重复消费
- 问题场景:在消息队列等异步处理场景中,可能会出现消息被重复消费的情况。例如,消费者在处理消息时,由于网络故障、进程崩溃等原因,导致消息处理未完成,但消息已经被标记为已消费,当消费者重新启动后,可能会再次消费该消息,从而造成数据不一致或业务逻辑错误。
- 解决方法
- 消息幂等性处理:与接口幂等性类似,可以在消息处理逻辑中加入幂等性判断。例如,给每条消息分配一个唯一的 ID,消费者在处理消息时,先根据消息 ID 检查是否已经处理过该消息,如果是,则直接跳过处理。
- 使用消息队列的特性:一些消息队列提供了消息去重的功能,如 RocketMQ 的事务消息和 Kafka 的幂等生产者。可以利用这些特性来确保消息在生产和消费过程中的唯一性。
- 消费状态记录:在数据库或缓存中记录消息的消费状态,消费者在处理消息前先查询状态,判断是否已经消费过。如果已经消费过,则不再处理。同时,要确保消费状态的记录和消息处理的操作在同一个事务中,以保证数据的一致性。
- 分布式锁:在处理消息时,先获取分布式锁,只有获取到锁的消费者才能处理消息。这样可以避免多个消费者同时处理同一条消息,从而防止重复消费。但使用分布式锁要注意避免死锁问题。
无论是实现接口幂等性还是解决重复消费问题,都需要根据具体的业务场景和技术架构选择合适的方法,以确保系统的稳定性和数据的准确性。
新增、更新会出现。
场景:1、用户重复点击多次 2、接口超时/重试 3、消息重复消费
解决方案:
- 数据库唯一索引 -- 防止新增脏数据
- Token+Redis机制防重 -- 防止页面重复提交( 第一次请求生成token,第二次发起请求后携带token,执行完成后删除。)
- 乐观锁 -- 基于版本号version实现, 在更新数据那一刻校验数据
- 分布式锁 -- redis(redisson、zookeeper) 性能低。
- 状态机 -- 状态变更, 更新数据时判断状态
- 异步请求携带唯一标识
面试:如何保证接口的幂等性?常见的实现方案有哪些?-腾讯云开发者社区-腾讯云
https://zhuanlan.zhihu.com/p/345428483
分布式唯一ID
概述
在分布式系统中,唯一 ID 是用于标识不同节点上的资源、事务或消息的标识符。设计分布式唯一 ID 需要考虑以下核心要素:
- 全局唯一性:确保所有节点生成的 ID 不重复
- 趋势递增 / 有序性:部分场景需要 ID 按时间有序生成
- 高性能:生成过程高效,避免成为系统瓶颈
- 高可用:确保分布式环境下持续生成 ID
- 安全性:ID 不可预测,避免被猜测
常见实现方案
1. UUID (Universally Unique Identifier)
原理:基于时间戳、MAC 地址或随机数生成 128 位标识符
优点:本地生成,无需依赖外部服务,性能极高
缺点:无序字符串,索引效率低;占用空间大 (16 字节)
Java 实现:
import java.util.UUID;public class UUIDGenerator {public static String generate() {return UUID.randomUUID().toString().replace("-", "");}
}
2. 数据库自增 ID
原理:利用数据库的 AUTO_INCREMENT 特性生成唯一 ID
优点:实现简单,有序性好
缺点:单点故障风险;性能瓶颈 (依赖数据库事务)
优化方案:
- 号段模式:数据库预分配多个 ID 段,应用本地缓存使用
- 双 Buffer 优化:当前号段使用到一定比例时,异步预取下一号段
3. Snowflake 算法(雪花算法)
原理:Twitter 开源的 64 位长整型 ID 生成算法,结构如下:
- 1 位符号位(始终为 0)
- 41 位时间戳(毫秒级,支持约 69 年)
- 5 位数据中心 ID(最多 32 个)
- 5 位机器 ID(每个数据中心最多 32 台机器)
- 12 位序列号(同一毫秒内生成的 ID,最多 4096 个)
雪花算法是 64 位 的二进制,一共包含了四部分:
- 1位是符号位,也就是最高位,始终是0,没有任何意义,因为要是唯一计算机二进制补码中就是负数,0才是正数。
- 41位是时间戳,具体到毫秒,41位的二进制可以使用69年,因为时间理论上永恒递增,所以根据这个排序是可以的。
- 10位是机器标识,可以全部用作机器ID,也可以用来标识机房ID + 机器ID,10位最多可以表示1024台机器。
- 12位是计数序列号,也就是同一台机器上同一时间,理论上还可以同时生成不同的ID,12位的序列号能够区分出4096个ID。
优点:高性能、趋势递增、可定制化
缺点:依赖系统时钟(需处理时钟回拨问题)
Java 实现:
public class SnowflakeIdGenerator {private final long startTimeStamp = 1609459200000L; // 2021-01-01 00:00:00 UTCprivate final long dataCenterIdBits = 5L;private final long machineIdBits = 5L;private final long sequenceBits = 12L;private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);private final long maxMachineId = -1L ^ (-1L << machineIdBits);private final long machineIdShift = sequenceBits;private final long dataCenterIdShift = sequenceBits + machineIdBits;private final long timestampLeftShift = sequenceBits + machineIdBits + dataCenterIdBits;private final long sequenceMask = -1L ^ (-1L << sequenceBits);private final long dataCenterId;private final long machineId;private long sequence = 0L;private long lastTimestamp = -1L;public SnowflakeIdGenerator(long dataCenterId, long machineId) {if (dataCenterId > maxDataCenterId || dataCenterId < 0) {throw new IllegalArgumentException("DataCenter ID must be between 0 and " + maxDataCenterId);}if (machineId > maxMachineId || machineId < 0) {throw new IllegalArgumentException("Machine ID must be between 0 and " + maxMachineId);}this.dataCenterId = dataCenterId;this.machineId = machineId;}public synchronized long nextId() {long currentTimestamp = System.currentTimeMillis();// 处理时钟回拨if (currentTimestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - currentTimestamp) + " milliseconds");}if (currentTimestamp == lastTimestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当前毫秒内序列号用完,等待下一毫秒currentTimestamp = waitNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = currentTimestamp;return ((currentTimestamp - startTimeStamp) << timestampLeftShift) |(dataCenterId << dataCenterIdShift) |(machineId << machineIdShift) |sequence;}private long waitNextMillis(long lastTimestamp) {long timestamp = System.currentTimeMillis();while (timestamp <= lastTimestamp) {timestamp = System.currentTimeMillis();}return timestamp;}
}
4. Redis 原子操作
原理:利用 Redis 的原子自增命令 (INCR) 生成唯一 ID
优点:高性能、支持分布式
缺点:依赖 Redis 可用性;需处理持久化问题
Java 实现:
import redis.clients.jedis.Jedis;public class RedisIdGenerator {private final Jedis jedis;private final String key;public RedisIdGenerator(String host, int port, String key) {this.jedis = new Jedis(host, port);this.key = key;}public long nextId() {return jedis.incr(key);}public void close() {if (jedis != null) {jedis.close();}}
}
Incr 命令将 key 中储存的数字值增一 , Increment 英 /ˈɪŋkrəmənt/
decr key 递减
5. 数据库号段模式
原理:数据库存储当前号段的最大值和步长,应用批量获取号段本地使用
优点:高性能、减少数据库访问
缺点:号段预分配可能导致 ID 浪费
Java 实现:
import java.sql.*;
import java.util.concurrent.atomic.AtomicLong;public class SegmentIdGenerator {private final String jdbcUrl;private final String username;private final String password;private final String bizType;private final int step;private AtomicLong currentId;private AtomicLong maxId;public SegmentIdGenerator(String jdbcUrl, String username, String password, String bizType, int step) {this.jdbcUrl = jdbcUrl;this.username = username;this.password = password;this.bizType = bizType;this.step = step;this.currentId = new AtomicLong(0);this.maxId = new AtomicLong(0);}public synchronized long nextId() {if (currentId.get() >= maxId.get()) {// 号段用完,获取下一个号段refreshSegment();}return currentId.incrementAndGet();}private void refreshSegment() {try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {// 开启事务conn.setAutoCommit(false);// 查询当前号段String selectSql = "SELECT max_id FROM id_generator WHERE biz_type = ? FOR UPDATE";try (PreparedStatement pstmt = conn.prepareStatement(selectSql)) {pstmt.setString(1, bizType);try (ResultSet rs = pstmt.executeQuery()) {if (rs.next()) {long oldMaxId = rs.getLong("max_id");long newMaxId = oldMaxId + step;// 更新号段String updateSql = "UPDATE id_generator SET max_id = ? WHERE biz_type = ? AND max_id = ?";try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {updateStmt.setLong(1, newMaxId);updateStmt.setString(2, bizType);updateStmt.setLong(3, oldMaxId);int rows = updateStmt.executeUpdate();if (rows == 0) {throw new RuntimeException("Failed to update segment for bizType: " + bizType);}// 更新本地号段currentId.set(oldMaxId);maxId.set(newMaxId);}} else {// 初始化号段String insertSql = "INSERT INTO id_generator (biz_type, max_id, step) VALUES (?, ?, ?)";try (PreparedStatement insertStmt = conn.prepareStatement(insertSql)) {insertStmt.setString(1, bizType);insertStmt.setLong(2, step);insertStmt.setInt(3, step);insertStmt.executeUpdate();// 更新本地号段currentId.set(0);maxId.set(step);}}}}conn.commit();} catch (SQLException e) {throw new RuntimeException("Failed to get next segment", e);}}
}
6. ULID (Universally Unique Lexicographically Sortable Identifier)
原理:结合 UUID 和 Snowflake 优点,生成 128 位 ID:
- 48 位时间戳(毫秒级,有序)
- 80 位随机数(保证唯一性)
优点:字符串形式、有序性、高性能
Java 实现(需引入依赖):
import com.github.f4b6a3.ulid.UlidCreator;public class ULIDGenerator {public static String generate() {return UlidCreator.getUlid().toString();}
}
Maven 依赖:
<dependency><groupId>com.github.f4b6a3</groupId><artifactId>ulid-creator</artifactId><version>5.0.0</version>
</dependency>
选择建议
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
UUID | 本地生成、简单 | 无序、占用空间大 | 缓存键、日志追踪 |
数据库自增 ID | 有序、简单 | 单点故障、性能低 | 小规模系统、数据库主键 |
Snowflake | 高性能、有序 | 时钟回拨问题 | 高并发场景(如订单 ID) |
Redis INCR | 高性能、分布式 | 依赖 Redis | 分布式计数器、全局 ID |
号段模式 | 高性能、数据库压力小 | 号段浪费 | 中大规模系统 |
ULID | 有序字符串、高性能 | 需引入依赖 | 分布式系统唯一标识 |
根据业务需求选择合适的方案,通常需要权衡性能、有序性、唯一性和系统复杂度。
RPC
RPC(Remote Procedure Call,远程过程调用)是一种允许程序调用远程计算机上的服务或方法的技术,使得远程调用看起来就像本地调用一样简单。以下是关于 RPC 的详细介绍:
一、核心概念
-
基本原理:
- 本地调用:程序直接调用本地函数 / 方法。
- 远程调用:通过网络将请求发送到远程服务器,服务器执行相应方法后返回结果。
-
核心组件:
- 客户端(Client):发起调用的程序。
- 服务端(Server):提供服务的程序。
- 客户端存根(Client Stub):负责将调用参数打包(序列化)并发送请求。
- 服务端存根(Server Stub):接收请求、解包参数并调用实际服务。
- 网络传输层:负责数据传输(如 TCP/IP、HTTP)。
-
关键特性:
- 透明性:调用远程方法与本地方法语法一致。
- 可靠性:处理网络异常、超时等问题。
- 高性能:减少网络开销,优化序列化 / 反序列化。
二、常见 RPC 框架
-
gRPC:
- 语言:支持多语言(Java、Python、Go 等)。
- 协议:基于 HTTP/2,使用 Protocol Buffers 序列化。
- 特点:高性能、强类型、支持流式通信。
-
Apache Dubbo:
- 语言:主要支持 Java。
- 协议:支持多种协议(Dubbo、REST、gRPC 等)。
- 特点:服务治理能力强(负载均衡、服务发现、熔断等)。
-
Thrift:
- 语言:多语言支持。
- 协议:二进制协议,支持多种传输层。
- 特点:Facebook 开源,适合跨语言场景。
-
JSON-RPC/XML-RPC:
- 协议:基于 JSON/XML 的简单 RPC 协议。
- 特点:轻量、易实现,适合简单场景。
-
Spring Cloud:
- 语言:主要支持 Java。
- 协议:基于 HTTP(RESTful)。
- 特点:微服务生态完善(服务发现、配置中心等)。
三、工作流程
-
客户端:
- 调用本地代理方法(Client Stub)。
- 参数序列化(如转为 JSON、Protobuf)。
- 通过网络发送请求到服务端。
-
服务端:
- 接收请求并反序列化参数。
- 调用实际服务方法。
- 将结果序列化并返回给客户端。
四、核心技术
-
序列化与反序列化:
- 将对象转换为字节流以便传输,常见方式:
- 文本格式:JSON、XML。
- 二进制格式:Protocol Buffers、Thrift、Kryo。
- 语言特定:Java 序列化、Python Pickle。
- 将对象转换为字节流以便传输,常见方式:
-
服务发现:
- 客户端如何找到服务端?
- 静态配置:硬编码服务地址。
- 注册中心:服务启动时注册到中心(如 ZooKeeper、Nacos、Consul)。
- 客户端如何找到服务端?
-
负载均衡:
- 多个服务实例时,如何选择?
- 轮询:按顺序依次选择。
- 随机:随机选择一个实例。
- 加权:根据实例性能分配权重。
- 多个服务实例时,如何选择?
-
熔断与限流:
- 熔断:当服务不可用时,快速失败避免级联故障。
- 限流:限制请求速率,保护服务不被压垮。
五、优缺点
- 优点:
- 简化分布式系统开发,降低复杂度。
- 提高开发效率,屏蔽网络细节。
- 支持跨语言、跨平台调用。
- 缺点:
- 增加系统复杂度(网络延迟、故障处理)。
- 调试和排查问题难度大。
- 过度依赖 RPC 可能导致服务间耦合度高。
六、Java 实现示例(gRPC)
- 定义服务接口(.proto 文件):
syntax = "proto3";package example;// 定义请求和响应消息
message HelloRequest {string name = 1;
}message HelloResponse {string message = 1;
}// 定义服务
service Greeter {rpc SayHello (HelloRequest) returns (HelloResponse);
}
-
生成代码:
- 使用
protoc
编译.proto 文件,生成客户端和服务端代码。
- 使用
-
服务端实现:
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;public class HelloServer {private Server server;private void start() throws Exception {server = ServerBuilder.forPort(50051).addService(new GreeterImpl()).build().start();System.out.println("Server started, listening on " + 50051);}private static class GreeterImpl extends GreeterGrpc.GreeterImplBase {@Overridepublic void sayHello(HelloRequest req, StreamObserver<HelloResponse> responseObserver) {HelloResponse reply = HelloResponse.newBuilder().setMessage("Hello " + req.getName()).build();responseObserver.onNext(reply);responseObserver.onCompleted();}}public static void main(String[] args) throws Exception {final HelloServer server = new HelloServer();server.start();server.blockUntilShutdown();}private void blockUntilShutdown() throws InterruptedException {if (server != null) {server.awaitTermination();}}
}
- 客户端实现:
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;public class HelloClient {private final ManagedChannel channel;private final GreeterGrpc.GreeterBlockingStub blockingStub;public HelloClient(String host, int port) {channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();blockingStub = GreeterGrpc.newBlockingStub(channel);}public void shutdown() throws InterruptedException {channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);}public void greet(String name) {HelloRequest request = HelloRequest.newBuilder().setName(name).build();HelloResponse response;try {response = blockingStub.sayHello(request);} catch (StatusRuntimeException e) {System.err.println("RPC failed: " + e.getStatus());return;}System.out.println("Greeting: " + response.getMessage());}public static void main(String[] args) throws Exception {HelloClient client = new HelloClient("localhost", 50051);try {String user = "World";if (args.length > 0) {user = args[0];}client.greet(user);} finally {client.shutdown();}}
}
七、适用场景
- 微服务架构中不同服务间的通信。
- 分布式系统中跨节点的方法调用。
- 异构系统间的集成(如 Java 服务调用 Python 服务)。
八、对比 REST API
特性 | RPC | REST API |
---|---|---|
调用方式 | 像本地方法一样调用 | 通过 HTTP 请求调用 |
协议 | 自定义协议(如 HTTP/2) | 基于 HTTP 协议 |
数据格式 | 二进制(如 Protobuf) | 文本(如 JSON/XML) |
性能 | 通常更高(二进制) | 较低(文本格式) |
灵活性 | 较低(强类型) | 较高(URL 资源导向) |
跨语言 | 需生成代码 | 天然支持 |
适用场景 | 内部服务调用 | 对外 API 暴露 |
根据场景选择合适的通信方式,两者并非互斥,可结合使用。
Protocol Buffers vs Thrift
Protocol Buffers(简称 Protobuf)和 Apache Thrift 都是用于序列化结构化数据的高性能框架,广泛应用于分布式系统和 RPC 通信中。以下是它们的详细对比:
一、核心特性对比
特性 | Protocol Buffers | Thrift |
---|---|---|
开发公司 | Google(2001 年) | Facebook(2007 年)→ Apache |
语言支持 | 多语言(Java、Python、Go、C++ 等) | 多语言(Java、Python、Go、C++、PHP 等) |
协议类型 | 仅序列化协议 | 序列化协议 + RPC 框架 |
IDL 语法 | .proto 文件,简洁 | .thrift 文件,更复杂(支持服务定义) |
序列化速度 | 极快(二进制编码优化) | 快(略慢于 Protobuf) |
压缩率 | 高(二进制格式紧凑) | 高(二进制格式) |
版本兼容性 | 良好(支持字段增删改,需遵循规则) | 良好(支持字段增删改,需遵循规则) |
元数据 | 需生成代码(.proto → 语言特定类) | 需生成代码(.thrift → 语言特定类) |
扩展能力 | 通过oneof 、map 等灵活扩展 | 通过struct 、union 、service 扩展 |
二、序列化性能
- Protobuf:
- 二进制格式更紧凑,序列化速度更快。
- 生成的代码更优化,内存占用更小。
- Thrift:
- 性能略低于 Protobuf,但支持多种传输协议(如 Binary、Compact、JSON)。
三、IDL(接口定义语言)
Protobuf 示例
syntax = "proto3";message Person {string name = 1;int32 id = 2;string email = 3;
}
Thrift 示例
struct Person {1: required string name,2: required i32 id,3: optional string email
}service PersonService {Person getPerson(1: i32 id),void savePerson(1: Person person)
}
四、RPC 集成
- Protobuf:
- 需结合 gRPC 等 RPC 框架使用。
- gRPC 基于 HTTP/2,提供高性能、流式通信能力。
- Thrift:
- 内置 RPC 支持,提供多种传输协议和服务模型。
- 支持同步、异步、单线程、多线程服务模型。
五、适用场景
场景 | Protobuf + gRPC | Thrift |
---|---|---|
内部微服务通信 | 推荐(高性能、强类型) | 推荐(内置 RPC,多语言支持) |
异构系统集成 | 推荐(跨语言支持) | 推荐(跨语言支持更丰富) |
需要多种协议 | 需额外开发(仅支持 HTTP/2) | 内置多种协议(Binary、Compact、JSON) |
快速开发 | 需分别实现序列化和 RPC | 一站式解决方案(IDL 定义服务) |
大数据量传输 | 推荐(压缩率高、性能优) | 推荐(性能接近) |
六、优缺点总结
Protocol Buffers
- 优点:
- 性能卓越,序列化速度快。
- 社区活跃,支持语言广泛。
- 与 gRPC 深度集成,生态完善。
- 缺点:
- 仅专注于序列化,需额外集成 RPC 框架。
- 二进制格式可读性差,调试复杂。
Thrift
- 优点:
- 一站式解决方案(序列化 + RPC)。
- 支持更多传输协议和服务模型。
- 内置服务发现和负载均衡能力。
- 缺点:
- 性能略低于 Protobuf。
- 社区活跃度低于 Protobuf。
七、选择建议
-
优先选择 Protobuf + gRPC:
- 若项目已采用 gRPC 或需要极致性能。
- 仅需序列化功能,计划自行集成 RPC 框架。
- 主要使用 Java、Go、Python 等主流语言。
-
优先选择 Thrift:
- 需要一站式 RPC 解决方案。
- 需支持 PHP、Ruby 等小众语言。
- 需要灵活选择传输协议(如 JSON、Binary)。
-
混合使用:
- 不同服务间根据需求选择(如内部服务用 Protobuf,对外服务用 Thrift)。
八、性能对比测试
测试场景 | Protobuf (ms) | Thrift (ms) | JSON (ms) |
---|---|---|---|
序列化 1000 对象 | 12 | 15 | 35 |
反序列化 1000 对象 | 8 | 10 | 40 |
数据大小(KB) | 28 | 32 | 45 |
(数据来源:第三方性能测试,仅供参考)
九、总结
两者都是优秀的序列化框架,选择取决于具体需求:
- Protobuf:性能之王,适合追求极致性能的场景,需与 RPC 框架结合。
- Thrift:功能全面,适合需要一站式解决方案的场景,支持更多协议和语言。
客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
比较正式的描述是:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
RPC: (Remote Procedure Call) 采用客户端/服务器方式(请求/响应),发送请求到服务器端,
服务端执行方法后返回结果。优点是跨语言跨平台,缺点是编译期无法排错,只能在运行时检查。
常见的 RPC 框架:阿里的 Dubbo,Google 开源的 gRpc,百度开源的 brpc,Dubbo等。
RPC 组成架构
一个完整的 RPC 架构里包含了四个核心的组件,分别是 Client、Client Stub、Server 以及 Server Stub,
这个 Stub 可以理解为存根。
- Client(客户端):服务的调用者
- Client Stub(客户端存根):存放服务端的地址消息,
再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方
- Server(服务端):服务的提供者
- Server Stub(服务端存根):接收客户端发送过来的消息,将消息解包,并调用本地的方法
链接:https://juejin.cn/post/7029481269415116813
总结:
分布式系统是由多个通过网络连接的独立计算机节点组成的系统,这些节点相互协作,共同完成任务,对外表现为一个单一系统。分布式系统的特点包括分布性、并发性、故障独立性和透明性。然而,分布式系统也面临网络延迟、数据一致性、故障处理等挑战。常见的应用场景包括大型网站、大数据处理和云计算平台。分布式事务的实现方案包括两阶段提交(2PC)、三阶段提交(3PC)、TCC、消息队列和Saga模式等。接口的幂等性和解决重复消费问题在分布式系统中尤为重要,常见的解决方案包括数据库唯一索引、Token+Redis机制、乐观锁、分布式锁和状态机等。分布式唯一ID的生成方案有UUID、数据库自增ID、Snowflake算法、Redis原子操作、数据库号段模式和ULID等。RPC(远程过程调用)技术允许程序调用远程计算机上的服务,常见的RPC框架包括gRPC、Apache Dubbo、Thrift和Spring Cloud等。RPC的核心组件包括客户端、服务端、客户端存根和服务端存根,其工作流程涉及参数序列化、网络传输和结果反序列化。RPC的优缺点包括简化分布式系统开发、提高开发效率,但也增加了系统复杂度和调试难度。
相关文章:
分布式1(cap base理论 锁 事务 幂等性 rpc)
目录 分布式系统介绍 一、定义与概念 二、分布式系统的特点 三、分布式系统面临的挑战 四、分布式系统的常见应用场景 CAP 定理 BASE 理论 BASE理论是如何保证最终一致性的 分布式锁的常见使用场景有哪些? 1. 防止多节点重复操作 2. 资源互斥访问 3. 分…...
如何下载 MySQL 驱动 JAR 包
下载 MySQL 驱动 JAR 包的 4 种方式: 官网下载:从 MySQL 官网 下载对应版本的 ZIP 包Maven:在 pom.xml 添加 mysql-connector-java 依赖Gradle:在 build.gradle 使用 implementation mysql:mysql-connector-java:8.0.x直接下载&a…...
unordered_map和unordered的介绍和使用
目录 unordered系列关联式容器 unordered_map unordered_map的接口说明 unordered_map的定义方式 unordered_map接口的使用 unordered_map的容量 unordered_map的迭代器 unordered_map的元素访问 unordered_map的查询 unordered_map的修改操作 unordered_multimap u…...
【原创】使用阿里云存放一个临时共享的文件
在某些场合,需要临时将一个文件存储到一个可被公网访问的地方,某个服务需要访问一下这个文件。这个文件基本上就是一次寿命,也就是你上传一下,然后被访问一下,这个文件的寿命就结束了。 对于这种需求,自建…...
C++学习细节回顾(汇总二)
一.初始化列表相关 1.初始化顺序受申明顺序影响 2.在必要时可以部分不采用初始化列表,避免受特性1影响 二.非类型模板参数 template< class T , size_t N 10 > 三.特化–特殊化处理 template< class T > bool less(T left , T right) { return left&…...
【行为型之访问者模式】游戏开发实战——Unity灵活数据操作与跨系统交互的架构秘诀
文章目录 🧳 访问者模式(Visitor Pattern)深度解析一、模式本质与核心价值二、经典UML结构三、Unity实战代码(游戏物品系统)1. 定义元素与访问者接口2. 实现具体元素类3. 实现具体访问者4. 对象结构管理5. 客户端使用 …...
ubuntu系统安装配置adb工具
获取adb tools 1. 下载最新版(替换链接中的版本号) wget https://dl.google.com/android/repository/platform-tools-latest-linux.zip2. 解压到当前目录 unzip platform-tools-latest-linux.zip3. 移动文件到系统目录 sudo mv platform-tools /usr/…...
基于Python的量化交易实盘部署与风险管理指南
基于Python的量化交易实盘部署与风险管理指南 一、模拟交易与参数优化 1.1 券商API接入与模拟交易 在量化交易落地前,模拟交易是策略验证的“安全沙箱”,其核心价值在于用零成本环境暴露策略缺陷。以股票市场为例,同花顺与通达信模拟盘接口…...
MySQL 入门大全:数据类型
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
WPF Datagrid 数据加载和性能
这篇文章并非讨论 WPF Datagrid 的性能数据,而只是简单介绍一下为了使其性能良好,你需要注意哪些方面。我不太想使用性能分析器来展示实际数据,而是尽可能地使用了 Stopwatch 类。这篇文章不会深入探讨处理海量数据的技术,例如分页…...
商城小程序源码介绍
今天要为大家介绍一款基于ThinkPHP、FastAdmin以及UniApp开发的商城小程序源码,这款源码在设计和功能上都有不俗的表现,非常适合想要搭建线上商城的开发者。 该源码采用了ThinkPHP作为后端框架,利用其强大的性能和灵活性,保障了系…...
【大模型】OpenManus 项目深度解析:构建通用 AI Agent的开源框架
OpenManus 项目深度解析:构建通用 AI Agent的开源框架 一、项目概述项目特点 二、项目运行方式与执行步骤(一)环境准备方法 1:使用 conda方法 2:使用 uv(推荐) (二)配置&…...
数据科学和机器学习的“看家兵器”——pandas模块 之一
目录 pandas 模块介绍 4.1 pandas 数据结构 一、课程目标 二、Series 对象介绍 三、DataFrame 对象介绍 四、Series 和 DataFrame 在具体应用中的注意事项 (一)Series 注意事项 (二)DataFrame 注意事项 五、实战案例 案例 1:学生成绩分析 案例 2:销售数据分析 案例 3:股…...
Linux服务之lvs集群与dr模式部署
目录 一.lvs相关概述 1.lvs集群的工作模式 2.lvs调度算法 3.ipvsadm工具 二.DR模式部署 一.lvs相关概述 1.lvs集群的工作模式 lvs-nat:修改请求报文的目标IP,多目标IP的DNAT lvs-dr:操纵封装新的MAC地址(直接路由)lvs-tu…...
LVS负载均衡群集和keepalive
目录 一. 集群概述 1.1 集群的定义 1.2 集群的分类 1. 高可用集群 HA 2. 高性能运输群集 HPC 3.负载均衡群集 LB 4. 分布式存储集群 二. LVS概述 2.1 LVS的定义 2.2 LVS的工作原理 2.3 LVS 的三种工作模式 2.4 LVS 三种工作模式的对比 2.5 LVS 调度算法 1. 静态…...
MCU裸机程序如何移植到RTOS?
目录 1、裸机编程 2、实时操作系统 3、移植裸机程序到RTOS的步骤 步骤1:分析裸机代码 步骤2:选择并设置RTOS环境 步骤3:设计任务架构 步骤4:实现任务间通信 步骤5:处理硬件交互 步骤6:测试和调试 …...
从入门到精通:阿里云/腾讯云服务器深度优化实践
在当今数字化浪潮中,云计算已成为企业IT基础设施的核心选择。作为国内云计算领域的两大头部厂商,阿里云与腾讯云凭借各自的技术积累和生态优势,持续吸引着不同行业用户的关注。本文将带您从基础配置到高级优化,全面掌握阿里云/腾讯…...
机器学习基础课程-5-课程实验
5.1 实验介绍 实验背景 在这个项目中,您将使用1994年美国人口普查收集的数据,选用几个监督学习算法以准确地建模被调查者的收入。然后,您将根据初步结果从中选择出最佳的候选算法,并进一步优化该算法以最好地建模这些数据。你的目…...
生成对抗网络(Generative Adversarial Networks ,GAN)
生成对抗网络是深度学习领域最具革命性的生成模型之一。 一 GAN框架 1.1组成 构造生成器(G)与判别器(D)进行动态对抗,实现数据的无监督生成。 G(造假者):接收噪声 ,…...
Linux——CMake的快速入门上手和保姆级使用介绍、一键执行shell脚本
目录 一、前言 二、CMake简介 三、CMake与其他常见的构建、编译工具的联系 四、CMake入门 1、CMake的使用注意事项 2、基本的概念和术语 3、CMake常用的预定义变量 4、CMakeLists.txt文件的基本结构 五、上手实操 1、示例 编辑 2、一个正式的工程构建 2.1基本构…...
GAN简读
Abstract 我们提出了一个通过同时训练两个模型的对抗过程来评估生成模型的新框架:一个生成模型 G G G用来捕捉数据特征,还有一个用于估计这个样本是来自训练样本还是 G G G的概率的判别模型 D D D, G G G的训练过程是最大化 D D D犯错的概率。这个框架就相当于一个minimax tw…...
Jsp技术入门指南【十四】实现基于MySQL+JDBC+JSP数据库验证的登录界面与登录跳转功能
Jsp技术入门指南【十四】实现基于MySQLJDBCJSP数据库验证的登录界面与登录跳转功能 前言第一步:加入驱动包与Maven第二步、创建并导入web库第三步、连接本地数据库的java代码核心代码讲解 第四步、创建数据库第五步、导入并修改JSP登录文件 前言 在之前的博客中&am…...
【漫话机器学习系列】259.神经网络参数的初始化(Initialization Of Neural Network Parameters)
神经网络参数初始化详解 在构建神经网络时,参数的初始化虽然只是一个开端步骤,但它对网络最终的训练效果和收敛速度有着至关重要的影响。本文将结合一张手绘风格图,深入浅出地讲解神经网络初始化的背景、方法及其数学依据,帮助大…...
如何设置FFmpeg实现对高分辨率视频进行转码
使用FFmpeg进行高分辨率视频转码的步骤如下:首先,确保FFmpeg支持GPU加速,通过命令ffmpeg -hwaccels检查CUDA支持。接着,下载样本视频进行测试,例如使用wget命令获取Blender基金会的样本视频。然后,使用FFmp…...
2025tg最新免费社工库机器人
中情局社工库 https://t.me/ZhongQingJuSGKBOT?start07c662145624d195aa098f0d39e6451d 小孩哥社工库 http://t.me/xiaohaigeSGK1_bot?startWGGVVrMgQiBslNE 冰墩墩个户机器人 t.me/bingdundung… 维基百科社工库 https://t.me/WikiSGKBot?start0b9d27c2e91b AI社工库…...
ps向pl传数据axi-4-lite
定义一个axi-4-lite ip,引出管脚 可以看到,ip的地址是这个 因为在定义axi-4-lite ip的时候定义了4个寄存器,其中只把第2个引出来,所以只需要往第2个写数据就可 即只有 (u32)(0x800000004) angle; 这个是有效的 这时pl就可以收到angle的值…...
scikit-learn在无监督学习算法的应用
哈喽,我是我不是小upper~ 前几天,写了一篇对scikit-learn在监督学习算法的应用详解,今天来说说关于sklearn在无监督算法方面的案例。 稍微接触过机器学习的朋友就知道,无监督学习是在没有标签的数据上进行训练的。其主要目的可能…...
聊聊JetCache的缓存构建
序 本文主要研究一下JetCache的缓存构建 invokeWithCached com/alicp/jetcache/anno/method/CacheHandler.java private static Object invokeWithCached(CacheInvokeContext context)throws Throwable {CacheInvokeConfig cic context.getCacheInvokeConfig();CachedAnnoC…...
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
问题场景: 提示:ipa是用于苹果设备安装的软件包资源 设备:iphone 13(未越狱) 安装包类型:ipa包 调试工具:hbuilderx 问题描述 提要:ios包无法安装 uniapp导出ios包无法安装 相信有小伙伴跟我一样&…...
浅析 Golang 内存管理
文章目录 浅析 Golang 内存管理栈(Stack)堆(Heap)堆 vs. 栈内存逃逸分析内存逃逸产生的原因避免内存逃逸的手段 内存泄露常见的内存泄露场景如何避免内存泄露?总结 浅析 Golang 内存管理 在 Golang 当中,堆…...
仿射变换 与 透视变换
仿射变换 与 透视变换 几种变换之间的关系 1、缩放 Rescale 1)变换矩阵 缩放变换矩阵,形为 : , 其中: 、 为 x轴 和 y轴的缩放因子,即 宽高的缩放因子 图像中的每一个像素点 (x, y),经过矩阵…...
Vue.js---嵌套的effect与effect栈
4.3嵌套的effect与effect栈 1、嵌套的effect effect是可以发生嵌套的 01 effect(function effectFn1() { 02 effect(function effectFn2() { /* ... */ }) 03 /* ... */ 04 })有这么一段代码: 01 // 原始数据 02 const data { foo: true, bar: true } 03 /…...
jQuery知识框架
一、jQuery 基础 核心概念 $ 或 jQuery:全局函数,用于选择元素或创建DOM对象。 链式调用:多数方法返回jQuery对象,支持连续操作。 文档就绪事件: $(document).ready(function() { /* 代码 */ }); // 简写 $(function…...
【Java学习笔记】hashCode方法
hashCode方法 注意:C要大写 作用:返回对象的哈希码值(可以当作是地址,真实的地址在 Java 虚拟机上),支持此方法是为了提高哈希表的性能 底层实现:实际上,由Object类定义的hashCod…...
[思维模式-37]:什么是事?什么是物?什么事物?如何通过数学的方法阐述事物?
一、基本概念 1、事(Event) “事”通常指的是人类在社会生活中的各种活动、行为、事件或情况,具有动态性和过程性,强调的是一种变化、发展或相互作用的流程。 特点 动态性:“事”往往涉及一系列的动作、变化和发展过程。例如&a…...
STM32-USART串口通信(9)
一、通信接口介绍 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。 当STM32想要实现一些功能,但是需要外挂一些其他模块才能实现,这就需要在两个设备之间连接上一根或多跟通信线,通过通信线路发送或者接…...
【内网渗透】——NTML以及Hash Relay
【内网渗透】——NTLM以及Hash Relay 文章目录 【内网渗透】——NTLM以及Hash Relay[toc]前情提要1.NTML网络认证机制1.1NTML协议1.2NET NTMLv21.3NTML的认证方式1.4NTLM hash的生成方法: 2.PTH(pass the hash)2.1原理2.2漏洞原理2.3实验环境2.4攻击过程…...
速查 Linux 常用指令 II
目录 一、网络管理命令1. 查看和配置网络设备:ifconfig1)重启网络命令2)重启网卡命令 2. 查看与设置路由:route3. 追踪网络路由:traceroute4. 查看端口信息和使用情况1)netstat 命令2)lsof 命令…...
基于 GPUGEEK平台进行vLLM环境部署DeepSeek-R1-70B
选择 GPUGEEK 平台的原因 算力资源丰富:GPUGEEK 提供多样且高性能的 GPU 资源,像英伟达高端 GPU 。DeepSeek - R1 - 70B 模型推理计算量巨大,需要强大算力支持,该平台能满足其对计算资源的高要求,保障推理高效运行。便…...
深入理解ThingsBoard的Actor模型
1、ThingsBoard系统中定义了哪些Actor ✅ ThingsBoard Actor 创建机制与作用对照表: Actor 类型 何时创建 由谁创建 是否缓存 作用描述 SystemActor 系统启动时 DefaultActorService / ActorSystem ✅ 是 ★ ThingsBoard 平台服务级别管理器:负责创建所有的Actor AppActor...
虚幻引擎5-Unreal Engine笔记之Qt与UE中的Meta和Property
虚幻引擎5-Unreal Engine笔记之Qt与UE中的Meta和Property code review! 文章目录 虚幻引擎5-Unreal Engine笔记之Qt与UE中的Meta和Property1.Qt 中的 Meta(元对象系统)1.1 主要功能1.2 如何实现1.2.1 例子1.2.2 访问 meta 信息 2.UE5 中的 Metaÿ…...
技术中台-核心技术介绍(微服务、云原生、DevOps等)
在企业数字化中台建设中,技术中台是支撑业务中台、数据中台及其他上层应用的底层技术基础设施,其核心目标是提供标准化、可复用的技术能力,降低业务开发门槛,提升系统稳定性与扩展性。技术中台的技术栈需覆盖从开发、运维到治理的…...
attention_weights = torch.ones_like(prompt_embedding[:, :, 0]):切片操作获取第二维度,第三维度
attention_weights = torch.ones_like(prompt_embedding[:, :, 0]):切片操作获取第1 维度,第二维度 attention_weights = torch.ones_like(prompt_embedding[:, :, 0]) 这行代码的作用是创建一个与 prompt_embedding[:, :, 0] 形状相同且所有元素都为 1 的张量,它用于初始化…...
2025年中国DevOps工具选型指南:主流平台能力横向对比
在数字化转型纵深发展的2025年,中国企业的DevOps工具选型呈现多元化态势。本文从技术架构、合规适配、生态整合三个维度,对Gitee、阿里云效(云效DevOps)、GitLab CE(中国版)三大主流平台进行客观对比分析&a…...
国产ETL数据集成软件和Informatica 相比如何
数据集成领域Informatica名号可谓无人不知无人不晓。作为国际知名的ETL工具,凭借其强大的功能和多年的市场积累,赢得了众多企业的信赖。然而,随着国内企业数字化转型的加速以及对数据安全、成本控制和本地化服务的需求日益增长,国…...
FFMPEG 与 mp4
1. FFmpeg 中的 start_time 与 time_base start_time 流的起始时间戳(单位:time_base),表示第一帧的呈现时间(Presentation Time)。通常用于同步多个流(如音频和视频)。 time_base …...
在RAG中 如何提高向量搜索的准确性?
在RAG(Retrieval-Augmented Generation)系统中,提高向量搜索的准确性需要从数据预处理、模型选择、算法优化和后处理等多个维度进行综合改进。以下是具体策略的详细分析: 一、优化数据质量与预处理 1. 数据清洗与结构化 去噪与规范化:去除停用词、拼写纠错、统一大小写和…...
Python调用SQLite及pandas相关API详解
前言 SQLite是一个轻量级的嵌入式关系数据库,它不需要独立的服务器进程,将数据存储在单一的磁盘文件中。Python内置了sqlite3模块,使得我们可以非常方便地操作SQLite数据库。同时,pandas作为Python数据分析的重要工具,…...
【Java学习笔记】finalize方法
finalize 方法 说明:实际开发中很少或者几乎不会重写finalize方法,更多的是应对面试考点 说明 (1)当对象被回收时,系统会自动调用该对象的 finalize 方法。子类可以重写该方法,做一些额外的资源释放操作&…...
MySQL之基础索引
目录 引言 1、创建索引 2、索引的原理 2、索引的类型 3、索引的使用 1.添加索引 2.删除索引 3.删除主键索引 4.修改索引 5.查询索引 引言 当一个数据库里面的数据特别多,比如800万,光是创建插入数据就要十几分钟,我们查询一条信息也…...