目录
1 网络通信
2 分布式和微服务的区别:
3 RPC,HTTP和HTTPS的区别联系
4 谈谈你对负载均衡的理解
5.介绍下CAP
6 分布式锁
6.1 概念
6.2 实现方式
6.3 应用场景
6.4 优缺点
6.5 什么是幂等,如何解决幂等性问题
6.6 你对一致性hash算法的理解
6.7 分布式ID有哪些设计方案
6.8 分布式锁实现方案
6.9 ZAP协议
6.10 Paxos算法
7 分布式事务
7.1 概念
7.2 分布式事务产生的场景
7.3 分布式事务解决方案
一、两阶段提交(2PC)
二、三阶段提交(3PC)
三、TCC(Try-Confirm-Cancel)
四、最终一致性
五、Seata
8.有哪些限流算法和工具类(框架)?
8.1 限流算法
8.2 限流工具/框架
参考链接:不再冗述
Redis学习-22.网络模型https://blog.csdn.net/weixin_33218697/article/details/145727266?spm=1011.2415.3001.5331
22.6 异步IO
分布式和微服务都是现代软件开发中常用的架构模式,它们各自具有独特的特点和优势,同时也存在一定的联系。下面我将详细解释它们之间的区别和联系。
分布式的定义、特点和优势
定义:
分布式系统是由多台计算机通过网络连接组成的系统,这些计算机协调工作,对用户表现为一个统一整体。
特点:
可扩展性:通过增加节点来扩展系统的处理能力。
容错性:部分节点失效不影响整体可用性。
透明性:包括访问、位置、并发等透明性,用户无需感知底层细节。
并发性:多节点可并行处理操作。
资源共享:硬件、软件、数据资源可被多用户共享。
优势:
高可靠性:通过冗余节点避免单点故障。
性能提升:分布式计算可分解任务并行处理。
弹性扩展:根据负载动态增减资源。
资源优化:整合闲置资源提高利用率。
成本效益:使用普通硬件集群替代昂贵大型机。
微服务的定义、特点和优势
定义:
微服务是一种软件架构风格,它将一个大型应用程序拆分为一组小型、独立、低耦合的服务,每个服务专注于完成单一特定的业务功能,并且每个服务都可以独立开发、部署和扩展。
特点:
服务细粒度:每个微服务通常对应着系统中的一个小的、独立的功能模块。
独立开发、部署与扩展:每个微服务可以独立选择最适合的技术栈进行开发、测试和部署。
低耦合:服务之间通过定义良好的接口进行通信,不依赖对方的内部实现细节。
高内聚:服务内部的功能紧密相关,便于理解和修改。
优势:
灵活性:开发团队可以更为灵活地开发和部署服务。
稳定性:由于服务粒度的减小,单个服务的复杂度大大降低,提高了系统的稳定性。
易维护性:每个服务代码量小、功能单一,便于维护和修改。
资源利用率高:通过容器化技术,提升了服务的独立性和系统的资源利用率。
分布式与微服务的联系和区别
联系:
都是解决大型复杂系统的手段:分布式和微服务都旨在解决大型复杂系统的开发、部署和管理问题。
都强调服务的独立性:分布式服务架构强调的是服务的分布式部署和通信,而微服务架构则侧重于将应用划分为一组小的、独立的服务。
都遵循松耦合和高内聚的设计原则:以减少服务间的依赖和提高可维护性。
区别:
服务粒度:分布式服务架构的服务粒度可能较大,而微服务架构的服务粒度更细。
部署方式:分布式服务架构可能将不同的服务部署在不同的服务器上,但不一定要求每个服务都独立部署。而微服务架构则要求每个服务都能独立开发、部署和扩展。
通信方式:分布式服务架构可能通过多种方式进行通信,而微服务架构通常通过轻量级通信机制(如HTTP/REST、gRPC、消息队列等)进行通信。
区别
联系
常见的RPC和Http框架:
RPC框架:
Http框架:
注意:
OpenFeign底层使用的是HTTP协议进行通信,而不是RPC
负载均衡是分布式系统和网络架构中的一个核心概念,它主要关注如何有效地分配网络或应用流量,以优化资源使用、提高系统性能和可靠性。以下是我对负载均衡的详细理解:
一、定义
负载均衡(Load Balancing)是一种将网络请求或数据流量分散到多个服务器或网络节点上的技术,以确保每个节点都能处理适量的请求,从而避免单点过载,提高整个系统的吞吐量和响应时间。
二、目的
负载均衡的主要目的是提高系统的可用性、可扩展性和性能。通过分散流量,负载均衡可以减少单个服务器的负载,防止服务器过载崩溃,同时提高系统的容错能力和弹性。此外,负载均衡还可以实现资源的优化利用,提高系统的整体效率和用户体验。
三、常见策略
轮转调度(Round-Robin Scheduling):
加权轮转调度(Weighted Round-Robin Scheduling):
随机均衡调度(Random Scheduling):
加权随机均衡调度(Weighted Random Scheduling):
最小连接调度(Least-Connection Scheduling):
加权最小连接调度(Weighted Least-Connection Scheduling):
目标地址散列调度(Destination Hashing Scheduling):
源地址散列调度(Source Hashing Scheduling):
基于局部性的最少链接调度(Locality-Based Least Connections Scheduling):
带复制的基于局部性最少链接调度(Locality-Based Least Connections with Replication Scheduling):
响应速度均衡调度(Response Time Scheduling):
处理能力均衡调度(Processing Capacity Scheduling):
DNS均衡调度(DNS Scheduling):
四、实现负载均衡的技术和工具
五、负载均衡如何与容器技术结合
使用容器编排工具的负载均衡功能
服务网格
反向代理
云服务提供商的负载均衡服务
CAP理论的三个属性
一致性(Consistency):
定义:所有节点在同一时间具有相同的数据值,即数据的更新对所有用户可见。
场景示例:在银行转账场景中,当用户A向用户B转账100元时,转账操作要么完全成功,要么完全失败,系统必须保证双方账户余额的一致性。
重要性:对于需要强一致性的系统,如金融交易系统、电子商务的订单支付等,一致性是至关重要的。一旦出现数据不一致,将导致严重的业务问题,如重复扣款或充值失败。
可用性(Availability):
定义:系统中的每个请求都会在合理的时间范围内收到非错误的响应,无论该响应是操作成功还是失败。
场景示例:在电商网站的高峰期,如“双11”购物节期间,系统需要能够处理大量的用户请求,保证用户可以正常浏览商品、下单购买等。
重要性:对于高并发、高流量的业务系统,如电商、社交网络等,可用性是保障用户体验的关键。如果系统出现不可用的情况,将导致用户流失和业务损失。
分区容错性(Partition Tolerance):
定义:分布式系统在遇到任何网络分区故障时,仍然能够正常运行。即系统能够容忍网络分区故障,继续提供服务。
场景示例:在大型分布式系统中,如跨地区、跨机房部署的云计算平台,网络可能出现故障,导致部分节点之间无法通信。系统需要在这种情况下仍然能够正常运行,保证数据的存储和访问。
重要性:在网络环境复杂的分布式系统中,分区容错性是保证系统可靠性的基础。由于网络故障是不可避免的,系统必须具备分区容错能力,以确保数据不会因局部故障而丢失或不可用。
CAP理论的应用和权衡
在分布式系统设计中,由于网络分区是不可避免的,因此需要在一致性和可用性之间做出权衡。具体来说,在网络分区发生时,系统可以选择:
CP模型:保证一致性和分区容错性,但可能牺牲部分可用性。适用于需要强一致性的场景,如金融交易系统、支付系统等。
(ZooKeeper )
AP模型:保证可用性和分区容错性,但可能牺牲一致性。适用于高并发、高流量的业务系统,如电商、社交网络等。这些系统通常可以接受一定的数据不一致性,以换取更高的可用性和吞吐量。
(Kafka, Cassandra,Redis Cluster(默认AP))
值得注意的是,虽然理论上存在CA模型(即同时保证一致性和可用性),但在现实世界的分布式系统中,由于网络分区几乎是不可避免的,因此实际上很难维持CA模型的全部优点。因此,在分布式系统设计中,通常需要根据具体的业务需求和技术约束来选择合适的CAP组合。
分布式锁通过在分布式环境下对共享资源进行加锁控制,确保同一时刻只有一个客户端能够访问该资源。就像在单进程环境中,锁用于保护临界区资源,防止多个线程同时访问导致数据不一致或其他并发问题一样,分布式锁在分布式系统中起到了类似的作用,只不过它跨越了多个节点和进程,为整个分布式系统提供了一种互斥访问的手段。
唯一性:分布式锁在整个分布式系统中必须是唯一的,即同一把锁不能被多个客户端同时持有。这是实现互斥访问的基础,通过唯一标识来区分不同的锁,确保每个锁都能准确地控制对应的共享资源。
可获取与释放:客户端需要能够主动获取锁,以表明它要访问共享资源,并且在访问完成后能够及时释放锁,以便其他客户端能够获取锁并访问资源。获取和释放锁的操作应该是原子性的,以避免出现中间状态导致锁的状态不一致。
锁的有效期:为了防止因客户端异常或其他原因导致锁被无限期持有,分布式锁通常需要设置一个有效期。当锁的持有时间超过有效期后,系统会自动释放锁,允许其他客户端获取,从而保证资源的可用性。
表锁方式:通过在数据库中创建一张锁表,包含锁的名称、持有锁的客户端信息、锁的创建时间等字段。当客户端要获取锁时,在表中插入一条记录,若插入成功则获取锁成功,否则表示锁已被占用。释放锁时,删除相应记录。这种方式实现简单,但在高并发场景下性能较差,因为每次获取和释放锁都需进行数据库读写操作,且会对整张表进行锁定,并发度低。
行锁方式:可以利用数据库的行级锁来实现分布式锁。假设存在一张用于存储锁信息的表,其中有一个字段表示锁的状态。当客户端尝试获取锁时,使用SELECT ... FOR UPDATE
语句来获取特定行的锁。如果查询到锁状态为未被占用,则获取锁成功,并更新锁状态为已占用;如果查询到锁已被占用,则获取锁失败。这种方式相比表锁方式,并发度有所提高,因为它只锁定了特定的行,而不是整张表。但它仍然存在性能问题,在高并发下数据库的压力较大。
使用 SETNX 命令:以 Redis 为例,SETNX
命令用于在键不存在时设置键的值。当客户端尝试获取分布式锁时,执行SETNX lock_key value
命令,其中lock_key
是锁的键,value
可以是客户端的唯一标识或其他相关信息。如果命令返回1
,表示设置成功,即获取锁成功;如果返回0
,表示键已存在,锁已被其他客户端获取。释放锁时,通过DEL lock_key
命令删除键。为防止锁被长时间占用,通常会给锁设置过期时间,可使用EXPIRE
命令或在SETNX
时通过参数直接设置。
Redisson 框架:Redisson 是一个在 Redis 的基础上实现的分布式锁和分布式同步器的框架。它提供了更丰富的功能和更便捷的使用方式。例如,Redisson 的可重入锁RLock
,可以像在 Java 中使用普通锁一样方便地在分布式环境中使用。它还支持锁的自动续期,当一个客户端持有锁的时间超过了设置的有效期的一半时,Redisson 会自动延长锁的有效期,避免因业务处理时间过长导致锁提前释放。
基于 Zookeeper 实现:Zookeeper 通过节点的创建和观察机制来实现分布式锁。客户端在 Zookeeper 的指定节点下创建一个临时顺序节点,然后获取该节点下所有子节点的列表,并判断自己创建的节点是否是序号最小的。如果是,则获取锁成功;否则,需要监听比自己序号小的前一个节点的删除事件。当监听到前一个节点被删除时,再次判断自己是否是序号最小的节点,若是则获取锁。释放锁时,只需删除自己创建的临时节点即可。Zookeeper 的分布式锁实现具有较高的可靠性和稳定性,因为 Zookeeper 能够保证数据的一致性和顺序性,并且在节点故障时能够自动进行选举和恢复。
基于 etcd 实现:etcd 是一个分布式键值存储系统,它也可以用于实现分布式锁。客户端通过在 etcd 中创建一个唯一的键,并设置租约(Lease)来表示锁。当客户端获取锁时,它会创建一个带有租约的键,如果创建成功,则获取锁成功。其他客户端在尝试获取锁时,会发现键已存在,从而获取锁失败。租约可以设置一个有效期,当租约到期后,键会自动被删除,相当于释放了锁。etcd 的分布式锁实现利用了其强大的分布式一致性算法和键值存储功能,能够提供高效、可靠的锁服务。
分布式锁在分布式系统中有着广泛的应用场景,包括但不限于:
高并发下的资源争抢:如秒杀活动中的库存扣减,通过分布式锁确保在每次扣减库存时,只有一个线程或服务实例能够对库存进行修改,从而避免并发冲突。
分布式任务调度:确保定时任务在整个集群中只有一个节点执行,避免多个实例同时执行相同任务导致的资源浪费。
数据一致性保障:控制跨服务数据修改顺序,确保数据的一致性。
全局唯一操作:如生成订单号或流水号,通过分布式锁确保在同一时刻,只有一个实例能够执行生成唯一ID的操作,从而保证ID的唯一性。
分布式配置更新:确保在更新配置时,只有一个进程或线程可以进行操作,从而避免配置更新冲突。
优点:
解决分布式环境下数据一致性的问题。
提高系统的可用性和稳定性。
通过分布式锁,可以避免多个客户端同时对同一个资源进行读写操作,从而减少系统的负载和资源消耗。
缺点:
实现复杂度高:分布式锁需要协调多个节点之间的操作,实现起来相对复杂,需要处理各种异常情况和网络延迟等问题。
性能开销大:由于分布式锁涉及到多个节点的协调和通信,因此相对于单机锁来说,性能开销较大。
可能存在死锁问题:在分布式环境下,由于多个客户端可能同时持有锁,如果处理不当,可能会导致死锁问题的发生。
在分布式系统和网络协议中,幂等性是一个重要的概念,它要求对一个资源发起一次请求和多次请求的效果是相同的。以下是几种常见的解决幂等性问题的方案和代码示例:
一、使用数据库唯一主键
工作原理:
利用数据库的唯一索引或主键约束来保证数据的唯一性。当重复数据尝试插入时,数据库会抛出异常,从而阻止重复操作。
适用场景:
适用于新增类请求,确保不会因重复提交而产生冗余数据。
代码示例:
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
order_number VARCHAR(50) NOT NULL UNIQUE, -- 唯一订单号
amount DECIMAL(10,2) NOT NULL, status VARCHAR(20) DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class OrderService {
private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
private static final String USER = "your_username";
private static final String PASS = "your_password";
public void createOrder(String orderNumber, int userId, double amount) {
String sql = "INSERT INTO orders(order_number, user_id, amount) VALUES(?, ?, ?)";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, orderNumber);
pstmt.setInt(2, userId);
pstmt.setDouble(3, amount);
pstmt.executeUpdate();
System.out.println("订单创建成功!");
} catch (SQLException e) {
if (e.getErrorCode() == 1062) {
System.out.println("订单已存在, 无法重新创建!");
}
System.out.println("数据库错误: " + e.getMessage());
}
}
public static void main(String[] args) {
OrderService service = new OrderService();
service.createOrder("ORDER123", 1, 100.0);
service.createOrder("ORDER123", 1, 100.0); // 重复提交 }
}
二、业务状态校验
工作原理:
根据业务ID的唯一性和业务处理的结果去做判断,但需要考虑原子性,否则会因并发问题导致幂等失效。解决途径包括加锁(单机或分布式锁)或采用现成方案(如Tomato)。
适用场景:
适用于更新类请求,确保业务状态的一致性。
代码示例(假设使用分布式锁):
// 使用Redisson实现分布式锁
RLock lock = redissonClient.getLock("order:" + orderId);
try { lock.lock();
// 业务逻辑处理
} finally {
lock.unlock();
}
三、数据库乐观锁
工作原理:
在更新数据时,会根据预先设置的乐观锁状态来判断是否被其他操作修改过。如果状态符合预期,则进行更新;反之,需重新进行处理,以保证数据一致性和操作的幂等性。
适用场景:
适用于并发更新场景,确保数据不会被重复修改。
代码示例:
在数据库中增加一个版本号字段(如version
),每次更新数据时检查版本号:
UPDATE orders SET status = ?, version = version + 1 WHERE order_number = ? AND version = ?;
public void updateOrderStatus(String orderNumber, String newStatus) {
String sql = "UPDATE orders SET status = ?, version = version + 1 WHERE order_number = ? AND version = ?"; // ... 省略数据库连接和查询当前版本号的代码 ...
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, newStatus);
pstmt.setString(2, orderNumber);
pstmt.setInt(3, currentVersion);
int affectedRows = pstmt.executeUpdate();
if (affectedRows == 0) {
throw new RuntimeException("更新失败,版本号不匹配");
}
} catch (SQLException e) {
// 处理SQL异常
}
}
四、防重Token令牌
工作原理:
在请求处理流程中生成一个唯一的Token,并将其与请求绑定。在后续请求中,通过检查Token的有效性来判断请求是否重复。
适用场景:
适用于需要防止重复提交的场景,如表单提交、支付请求等。
代码示例:
生成T