模拟验证 分布式CAP定理

一、回顾分布式CAP定理

CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。

  • 一致性:意味着所有节点同时看到相同的数据。简单来说,如果在一致的系统上执行读操作,它应该返回最近的写操作的值。即读取应该所有节点返回相同的数据,即最近写入的值。
  • 可用性:分布式系统中的可用性确保系统100%的时间保持运行。无论节点的单独状态如何,每个请求都会获得(非错误)响应。但请注意,这并不能保证响应包含最新的写入。
  • 分区容错性:此条件表明,无论消息在系统中的节点之间是否丢失或延迟,系统都不会失败。在分布式系统中,分区容错已经成为一种必需品,而不是一种选择。这是通过跨节点和网络的组合充分复制记录来实现的。

下面试着写一个分布式demo来验证CAP定理,项目结构如下 maven中两个模块distributed-A和distributed-B模拟分布式的两个节点,A节点和B节点;其中A节点8081端口启动,B节点8082端口;

向A节点写入数据,写入后向B节点同步该数据,同时访问B节点的数据,来验证CAP定理。

二、AP组合

分布式系统满足AP,即放弃一致性,也就是在数据同步的过程中对其中节点进行数据访问,返回的可能不是最新的数据。按照AP定理的说法,示例中只要A接收到写入的数据后,直接通过网络向B节点同步即可,因为网络有延迟和不可靠性,向B节点同步数据需要耗时,而这期间B节点返回一个非错误的响应。

java 复制代码
// A节点的PushController
@RestController
@Slf4j
public class APushController {
    
    // 模拟数据存储
    private List<String> dataList = new ArrayList<>();
    
    // 模拟A节点接收到数据写入
    @GetMapping("/push")
    public String push(@RequestParam("data") String data) {
        // 本地数据存储
        dataList.add(data);
        // 同时同步数据至B节点
        // 这里使用SpringBoot3官方推荐的RestClient进行http调用
        RestClient restClient = RestClient.create();
        String response = restClient.get()
                .uri("http://localhost:8082/sendData?data=" + data)
                .retrieve().body(String.class);
        log.info("同步数据返回响应:{}", response);
        return "success";
    }
}
java 复制代码
// B节点的PushController
@RestController
@Slf4j
public class PushController {

    // 模拟数据存储
    private List<String> dataList = new ArrayList<>();
    
    // 用于接收来自A节点的同步数据
    @GetMapping("/sendData")
    @SneakyThrows
    public String sendData(@RequestParam("data") String data) {
        log.info("接收到数据:{}", data);
        // 睡眠10s 模拟网络延迟
        Thread.sleep(10000);
        dataList.add(data);
        return "success";
    }
    
    // 访问数据
    @GetMapping("/getData")
    public List<String> getData() {
        return dataList;
    }
}

访问A节点localhost:8081/push?data=1数据写入1,写入成功后A节点调用B节点的/sendData接口进行数据同步,B节点接收到数据后Thread.sleep(10000);模拟网络延迟。

同步期间调用B节点localhost:8082/getData获取数据。

10s后再次访问localhost:8082/getData获取数据 此时已验证了CAP定理中的AP,虽然A节点在往B节点同步数据,但B节点依旧能正常向外提供服务,都会获得(非错误)响应,虽然不是最新的数据。

三、CP组合

分布式系统满足AP,即要求强一致性;即B节点应该返回的是最新写入的值1,而不是空列表。

对B节点的PushController进行稍加改造。

java 复制代码
// B节点的PushController
@RestController
@Slf4j
public class PushController {

    // 模拟数据存储
    private List<String> dataList = new ArrayList<>();

    private Object object = new Object();

    // 用于接收来自A节点的同步数据
    @GetMapping("/sendData")
    @SneakyThrows
    public String sendData(@RequestParam("data") String data) {
        log.info("接收到数据:{}", data);
        synchronized (object){
            Thread.sleep(10000);
            dataList.add(data);
        }
        return "success";
    }

    @GetMapping("/getData")
    public List<String> getData() {
        synchronized (object){
            return dataList;
        }
    }
}

此时B节点接收到A节点的同步数据,使用synchronized进行上锁,同时getData方法也进行上锁,并且锁的是同一个对象,这样两个方法就达到了互斥,保证了在调用getData获取数据时是最新的数据;在数据同步时getData请求会阻塞,导致B节点无法向外提供服务(或返回错误响应),满足了CP,牺牲了可用性。

同步期间向B节点访问数据的请求被阻塞 无法及时响应

同步完成之后响应最新的数据

四、总结

CAP定理 分布式系统中,一致性、可用性和分区容忍性无法同时满足,根据实际业务场景需求权衡选择,确定最合适的方案。

以上为个人对CAP定理的理解,若有错误,还望批评指正

相关推荐
曹牧5 小时前
C#:空合并运算符
java·前端·数据库
布吉岛的石头5 小时前
Java 程序员第 50 阶段2:预训练的目标函数:自回归语言建模本质
java·开发语言·人工智能·深度学习·回归·transformer
golang学习记6 小时前
IDEA 2026年的玩法已经完全不同
java·ide·intellij-idea
紧跟先前的步伐6 小时前
Go AI Agent 实战 05|Temperature 与生成参数:让模型输出可控
java·人工智能·golang
金銀銅鐵6 小时前
[Java] 展示添加元素时 ArrayList 的内部变化
java·后端·ai编程
斌糖雪梨7 小时前
spring auth 核心流程详解
java·后端·spring
文丑颜不良啊8 小时前
LeetCode201~225题目解析
java·leetcode
SL_staff10 小时前
4G设备掉线三层归因指南:SIM卡、模组、JVS-IOT网关配置精准锁定
java·物联网·产品
Wang's Blog10 小时前
Java 项目实战: 交友项目 - 黑名单:取消黑名单
java·windows·交友
杨杨杨大侠11 小时前
DeepSeek Harness:连 Agent Loop 都能替换的插件架构
java·人工智能·agent