raft111

chrono介绍_<chrono>-优快云博客

C++11 新的计时方法——std::chrono 大法好_c++ chrono 计时-优快云博客

基于raft的kvdb面试问题-优快云博客

RPC 和 序列化_rpc 序列化协议-优快云博客

基于raft的kvDB-优快云博客

一个基于protobuf的极简RPC - goyas - 博客园 (cnblogs.com)

详解 Protobuf 在 C++ 下 Message、enum、Service 的使用_proto 中的service-优快云博客

RPC框架:从原理到选型,一文带你搞懂RPC-优快云博客

Raft算法精读 - gatsby123 - 博客园 (cnblogs.com)

Raft 的 Figure 8 讲了什么问题?为什么需要 no-op 日志?_raft 重复提交-优快云博客

定时器

thread传参这有点mb

doHeartBeat():这块一点都没看懂

日志同步的时候为什么要考虑到快照,为什么不直接讨论prevlogindex() 和 getLastLogIndex()的三种情况

为什么不讨论args->prevlogindex() < getLastLogIndex()这种情况

接收快照

日志寻找匹配加速

raft初始化

做选举智能指针

 C++11 make_shared以及shared_ptr_make shared ptr-优快云博客

term 在 Raft 中起到了逻辑时钟的作用,它可以帮助 server 检测过期信息比如过期的 leader。每一个 server 都存储有 current term 字段。当 server 间通信的时候,会交换 current term,如果c andidate 或者 leader 发现了自己的 term 过期了,它会立刻转为 follower 状态。如果一个节点收到了一个含有过期的 term 的请求,它会拒绝该请求。

持久化

boost库

序列化有助于持久化存储和减少数据体积

将需要持久化的数据序列化转成字符串类型再写入磁盘。

能当上领导者日志已经是最新的了,要不然也当不上领导者

大概就是启动多个raft类,通过控制节点宕机并记录提交的命令的返回值来判断是否正确。 

定时器

用一个random_device生成的种子初始化随机数生成器

设定最小时间和最大时间。。在这个时间区间内生成

网上找的。。。

选举超时的随机时间300到500毫秒

心跳超时时间25毫秒 需要小一个量级。。

RPC

RPC(Remote Procedure Call Protocol)远程过程调用协议。可以让我们像调用本地函数一样调用远程函数。

protobuf

C++实现轻量级RPC分布式网络通信框架_c++ rpc-优快云博客

RPC框架基础概念篇_我在地铁里吃闸机-优快云博客

message 为消息类型,这是 protobuf 中最常用的类型。任意 message 都会在 protoc 编译后生成同名的类 

详解 Protobuf 在 C++ 下 Message、enum、Service 的使用_proto 中的service-优快云博客

 

创建一个TCP连接到指定的IP地址和端口上,用于实现RPC(远程过程调用)通信 

分布式网络通信框架(十五)——Mprpc项目总结-优快云博客

ckerk

跳表

【SkipList-CPP】跳表简介 (kamacoder.com)

使用 C++ 开发、基于跳表实现的轻量级键值数据库。实现了插入数据、删除数据、查询数据、数据展示等功能。

存储引擎是以有序链表作为基础构建的,其中每个节点都存储着一对键值对。这便构成了一个基于有序链表的简易键值(K-V)存储引擎。

增删改查

使用指针数组 记录每一个层待插入节点的前驱节点

该层中小于新节点且最接近新节点的节点

/**
 * 在跳表中插入一个新元素。
 * @param key 新元素的键。
 * @param value 新元素的值。
 * @return 如果元素已存在,返回 1;否则,进行插入操作并返回 0。
 */
template <typename K, typename V>
int SkipList<K, V>::insert_element(const K key, const V value) {
        Node<K, V> *current = this->_header;
    // 用于在各层更新指针的数组
    Node<K, V> *update[_max_level + 1];  // 用于记录每层中待更新指针的节点
    memset(update, 0, sizeof(Node<K, V> *) * (_max_level + 1));

    // 从最高层向下搜索插入位置
    for (int i = _skip_list_level; i >= 0; i--) {
        // 寻找当前层中最接近且小于 key 的节点
        while (current->forward[i] != NULL && current->forward[i]->get_key() < key) {
            current = current->forward[i]; // 移动到下一节点
        }
        // 保存每层中该节点,以便后续插入时更新指针
        update[i] = current;
    }

    // 移动到最底层的下一节点,准备插入操作
    current = current->forward[0];
    // 检查待插入的键是否已存在
    if (current != NULL && current->get_key() == key) {
        // 键已存在,取消插入
        return 1;
    }
    // 检查待插入的键是否已存在于跳表中
    if (current == NULL || current->get_key() != key) {
        // 通过随机函数决定新节点的层级高度
        int random_level = get_random_level();
        // 如果新节点的层级超出了跳表的当前最高层级
        if (random_level > _skip_list_level) {
            // 对所有新的更高层级,将头节点设置为它们的前驱节点
            for (int i = _skip_list_level + 1; i <= random_level; i++) {
                update[i] = _header;
            }
            // 更新跳表的当前最高层级为新节点的层级
            _skip_list_level = random_level;
        }
        
        Node<K, V> *inserted_node = create_node(key, value, random_level);
        // 在各层插入新节点,同时更新前驱节点的 forward 指针
        for (int i = 0; i <= random_level; i++) {
            // 新节点指向当前节点的下一个节点
            inserted_node->forward[i] = update[i]->forward[i];
            // 当前节点的下一个节点更新为新节点
            update[i]->forward[i] = inserted_node;
        }
        _element_count++;
    }
    return 0;
}

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>

template <typename K, typename V>
class Node {
public:
    Node() {}
    Node(K k, V v, int);
    ~Node();
    K get_key() const;
    V get_value() const;
    void set_value(V);
    Node<K, V> **forward;
    int node_level;
private:
    K key;
    V value;
};

template <typename K, typename V>
Node<K, V>::Node(const K k, const V v, int level) {
    this->key = k;
    this->value = v;
    this->node_level = level;
    this->forward = new Node<K, V> *[level + 1];
    memset(this->forward, 0, sizeof(Node<K, V> *) * (level + 1));
};

template <typename K, typename V>
Node<K, V>::~Node() {
    delete[] forward;
};

template <typename K, typename V>
K Node<K, V>::get_key() const {
    return key;
};

template <typename K, typename V>
V Node<K, V>::get_value() const {
    return value;
};

template <typename K, typename V>
void Node<K, V>::set_value(V value) {
    this->value = value;
};

// 跳表结构
template <typename K, typename V>
class SkipList {
public:
    SkipList(int);
    int get_random_level();
    Node<K, V> *create_node(K, V, int);
    int insert_element(K, V);
    bool search_element(K);
    void delete_element(K);
private:
    int _max_level;
    int _skip_list_level;
    Node<K, V> *_header;
    int _element_count;
};

template <typename K, typename V>
Node<K, V> *SkipList<K, V>::create_node(const K k, const V v, int level) {
    Node<K, V> *n = new Node<K, V>(k, v, level);
    return n;
}

template <typename K, typename V>
int SkipList<K, V>::insert_element(const K key, const V value) {
    Node<K, V> *current = this->_header;
    Node<K, V> *update[_max_level + 1];
    memset(update, 0, sizeof(Node<K, V> *) * (_max_level + 1));
    for (int i = _skip_list_level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->get_key() < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }
    current = current->forward[0];
    if (current != NULL && current->get_key() == key) {
        return 1;
    }
    if (current == NULL || current->get_key() != key) {
        int random_level = get_random_level();
        if (random_level > _skip_list_level) {
            for (int i = _skip_list_level + 1; i < random_level + 1; i++) {
                update[i] = _header;
            }
            _skip_list_level = random_level;
        }
        Node<K, V> *inserted_node = create_node(key, value, random_level);
        for (int i = 0; i <= random_level; i++) {
            inserted_node->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = inserted_node;
        }
        _element_count++;
    }
    return 0;
}

template <typename K, typename V>
bool SkipList<K, V>::search_element(K key) {
    Node<K, V> *current = _header;
    for (int i = _skip_list_level; i >= 0; i--)
    {
        while (current->forward[i] && current->forward[i]->get_key() < key) {
            current = current->forward[i];
        }
    }
    current = current->forward[0];
    if (current and current->get_key() == key) {
        return true;
    }
    return false;
}

template <typename K, typename V>
void SkipList<K, V>::delete_element(K key) {
    Node<K, V> *current = this->_header;
    Node<K, V> *update[_max_level + 1];
    memset(update, 0, sizeof(Node<K, V> *) * (_max_level + 1));

    for (int i = _skip_list_level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->get_key() < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }

    current = current->forward[0];
    if (current != NULL && current->get_key() == key) {
        for (int i = 0; i <= _skip_list_level; i++) {
            if (update[i]->forward[i] != current)
                break;
            update[i]->forward[i] = current->forward[i];
        }
        while (_skip_list_level > 0 && _header->forward[_skip_list_level] == 0) {
            _skip_list_level--;
        }
        delete current;
        _element_count--;
    }
    return;
}

template <typename K, typename V>
SkipList<K, V>::SkipList(int max_level) {
    this->_max_level = max_level;
    this->_skip_list_level = 0;
    this->_element_count = 0;
    K k;
    V v;
    this->_header = new Node<K, V>(k, v, _max_level);
};

template<typename K, typename V>
int SkipList<K, V>::get_random_level(){
    int k = 1;
    while (rand() % 2) {
        k++;
    }
    k = (k < _max_level) ? k : _max_level;
    return k;
};

int main() {
    int N, K, M;

    std::cin >> N >> K >> M;

    SkipList<int, int> *skiplist = new SkipList<int, int>(16);

    // 插入数据
    for (int i = 0; i < N; i++) {
        int k, v;
        std::cin >> k >> v;
        if (skiplist->insert_element(k, v) == 0) {
            std::cout << "Insert Success" << std::endl;
        } else {
            std::cout << "Insert Failed" << std::endl;
        }
    }

    // 删除数据
    for (int i = 0; i < K; i++) {
        int k;
        std::cin >> k;
        skiplist->delete_element(k);
    }

    // 查找数据
    for (int i = 0; i < M; i++) {
        int k;
        std::cin >> k;
        if (skiplist->search_element(k)) {
            std::cout << "Search Success" << std::endl;
        } else {
            std::cout << "Search Failed" << std::endl;
        }
    }
    return 0;
}

介绍

背景

分布式式的共识算法实现本身是一个比较严谨的过程,因为其本身的存在是为了多个服务器之间通过共识算法达成一致性的状态,从而避免单个节点不可用而导致整个集群不可用

  1. 领导者选举(Leader Election):在系统启动时或者当前领导者失效时,节点会发起选举过程。节点会在一个随机的超时时间内等待收到来自其他节点的心跳消息。如果在超时时间内没有收到心跳消息,节点就会成为候选人并发起选举。候选人向其他节点发送投票请求,并在得到大多数节点的投票后成为新的领导者。然后定时向其它节点发起心跳,告知其它节点集群中已经有领导者,保持自己的领导者地位。当leader宕机或产生网络分区,其它节点没有收到心跳,就会选举超时,发起新一轮选举。
  2. 日志复制(Log Replication):raft的主要功能就是保证各个节点日志的一致性。一旦领导者选举完成,新的领导者就会接收客户端的请求,并将更新的日志条目复制到其他节点。当大多数节点都成功复制了这些日志条目时,更新被认为是提交的,并且可以应用到节点的状态机中。
  3. 安全性(Safety):安全性是确保系统在面临各种异常情况时仍能保持一致性的保障。Raft算法通过确保在选举中只有一个领导者(单一领导者)、大多数节点的一致性以及只有领导者可以处理客户端请求等方式保证分布式系统的安全性。

使用场景:

etcd 是一个分布式键值存储数据库,etcd 使用 Raft 在多节点间进行数据同步,每个节点都拥有状态机数据。TiKV 是一个分布式的键值数据库,并且通过 Raft 协议保证了多副本数据一致性以及高可用。TiKV 底层使用 Multi Raft,将数据划分为多个 region(切片),每个 region 其实还是一个标准的 Raft 集群,对每个分区的数据实现了多副本高可用。

剩下的在文章看。。

 

https://github.com/youngyangyang04/KVstorageBaseRaft-cpp/tree/main/example/rpcExample

rpc http区别

RPC 使用场景(大型的网站,内部子系统较多、接口非常多的情况下适合使用 RPC):

而对于Web应用,HTTP是首选。

CAP

pasxos

 

性能

关于rpc

头部变长编码+自定义的压缩算法

根据数值的⼤⼩来动态地占⽤存储空间, 使得值⽐较⼩的数字占⽤较少的字节数, 值相对⽐ 较⼤的数字占⽤较多的字节数, 这即是变⻓整型编码的基本思想。

的函数调用都是同步的,也就是调用方在发起请求后就能得到结果(成功返回结果失败则抛出异常),中间不能去干其他事情

但是在分布式中这种模式的缺点也是非常的明显,第一个问题是网络通讯的延迟会严重的制约请求-响应式RPC的响应速度,使得请求吞吐量无法满足性能需要,大量的CPU时间会阻塞在等待请求的响应上;

首先去掉请求的返回值,所有的方法请求都定义为无返回结果,这样调用方在发出请求之后就可以继续干后面的事情了,而不需要再等待服务返回结果。同时针对服务接口定义一个Callback接口用于服务端向客户端发送请求结果和事件通知,这样服务器就可以主动向客户端发送消息了

RPC 和 序列化_rpc 序列化协议-优快云博客

figure8

同一下标的日志被复制两次 导致已经提交的日志被覆盖

Raft 的 Figure 8 讲了什么问题?为什么需要 no-op 日志?_raft 重复提交-优快云博客

负载均衡

一致性哈希

图解一致性哈希算法,看这一篇就够了! -阿里云开发者社区 (aliyun.com)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值