模拟验证 分布式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定理的理解,若有错误,还望批评指正

相关推荐
需要8267 分钟前
MySQL MVCC 与事务隔离级别:从一条 update 看版本链
java·数据库·spring boot·mysql·spring cloud
Memory_荒年7 分钟前
订单超时未支付?从“定时扫库”一步步到最终形态
java·后端
hai_android1 小时前
LruCache 图片浏览器内存缓存
android·java·kotlin
玩美移动1 小时前
AI Skin Analysis API 技术解析:文件上传、异步任务与结果读取
java·人工智能·python
我是小白呀1 小时前
19-Temporal项目实战:将客户开通流程迁移到持久执行架构
java·开发语言·人工智能·架构·workflow
千里码aicood1 小时前
基于Hadoop的机票价格波动分析系统的设计与实现
大数据·hadoop·分布式
步行cgn1 小时前
Spring 负责注入的注解详解
java·sql·spring
摇滚侠1 小时前
《On Java 中文版 基础卷》阅读笔记 安装 Java 和本书示例 02
java·开发语言·笔记
许彰午2 小时前
53-审计三表
java·低代码·架构
重生之小比特2 小时前
【C++进阶】红黑树的实现
java·开发语言·c++