brafteditor防抖_braft源码分析(一)选举和心跳保持部分

RAFT是一种新型易于理解的分布式一致性复制协议,由斯坦福大学的Diego Ongaro和John Ousterhout提出,作为RAMCloud项目中的中心协调组件。Raft是一种Leader-Based的Multi-Paxos变种,相比Paxos、Zab、View Stamped Replication等协议提供了更完整更清晰的协议描述,并提供了清晰的节点增删描述。RAFT是一种新型易于理解的分布式一致性复制协议,由斯坦福大学的Diego Ongaro和John Ousterhout提出,作为RAMCloud项目中的中心协调组件。Raft是一种Leader-Based的Multi-Paxos变种,相比Paxos、Zab、View Stamped Replication等协议提供了更完整更清晰的协议描述,并提供了清晰的节点增删描述。

一、使用braft

1.1注册并启动Server

braft需要运行在具体的brpc server里面,你可以让braft和你的业务共享同样的端口, 也可以将braft启动到不同的端口中。 brpc允许一个端口上注册多个逻辑Service, 如果你的Service同样运行在brpc Server里面,你可以管理brpc Server并且调用以下任意一个接口将braft相关的Service加入到你的Server中。这样能让braft和你的业务跑在同样的端口里面, 降低运维的复杂度。

int add_service(brpc::Server* server, const butil::EndPoint& listen_addr);

int add_service(brpc::Server* server, int port);

int add_service(brpc::Server* server, const char* const butil::EndPoint& listen_addr);

add_service的部分代码如下,负责把braft相关的service添加到brpc server里面。RaftServiceImpl主要和raft协议有关,有一些选举、append_entries和快照相关的接口。CliServiceImpl负责管理braft相关的工作,比如add_peer,get_leader,transfer_leader等操作。

int NodeManager::add_service(brpc::Server* server,

const butil::EndPoint& listen_address) {

...

if (0 != server->AddService(

new RaftServiceImpl(listen_address),

brpc::SERVER_OWNS_SERVICE)) {

LOG(ERROR) << "Fail to add RaftService";

return -1;

}

...

if (0 != server->AddService(new CliServiceImpl, brpc::SERVER_OWNS_SERVICE)) {

LOG(ERROR) << "Fail to add CliService";

return -1;

}

{

BAIDU_SCOPED_LOCK(_mutex);

_addr_set.insert(listen_address);

}

return 0;

}

1.2实现业务状态机

需要继承braft::StateMachine并且实现里面的接口

#include

class YourStateMachineImple : public braft::StateMachine {

protected:

// on_apply是*必须*实现的

// on_apply会在一条或者多条日志被多数节点持久化之后调用, 通知用户将这些日志所表示的操作应用到业务状态机中.

// 通过iter, 可以从遍历所有未处理但是已经提交的日志, 如果你的状态机支持批量更新,可以一次性获取多

// 条日志提高状态机的吞吐.

//

void on_apply(braft::Iterator& iter) {

for (; iter.valid(); iter.next()) {

// This guard helps invoke iter.done()->Run() asynchronously to

// avoid that callback blocks the StateMachine.

braft::AsyncClosureGuard closure_guard(iter.done());

// Parse operation from iter.data() and execute this operation

// op = parse(iter.data());

// result = process(op)

// The purpose of following logs is to help you understand the way

// this StateMachine works.

// Remove these logs in performance-sensitive servers.

LOG_IF(INFO, FLAGS_log_applied_task)

<< "Exeucted operation " << op

<< " and the result is " << result

<< " at log_index=" << iter.index();

}

}

// 当这个braft节点被shutdown之后, 当所有的操作都结束, 会调用on_shutdown, 来通知用户这个状态机不再被使用。

// 这时候你可以安全的释放一些资源了.

virtual void on_shutdown() {

// Cleanup resources you'd like

}

1.3构造braft::Node

一个Node代表了一个RAFT实例, Node的ID由两个部分组成:GroupId: 为一个string, 表示这个复制组的ID.

PeerId, 结构是一个EndPoint表示对外服务的端口, 外加一个index(默认为0). 其中index的作用是让不同的副本能运行在同一个进程内, 在下面几个场景中,这个值不能忽略:

Node(const GroupId& group_id, const PeerId& peer_id);

启动这个节点:

// Starts this node

int start() {

butil::EndPoint addr(butil::my_ip(), FLAGS_port);

braft::NodeOptions node_options;

if (node_options.initial_conf.parse_from(FLAGS_conf) != 0) {

LOG(ERROR) << "Fail to parse configuration `" << FLAGS_conf << '\'';

return -1;

}

node_options.election_timeout_ms = FLAGS_election_timeout_ms;

node_options.fsm = this;

node_options.node_owns_fsm = false;

node_options.snapshot_interval_s = FLAGS_snapshot_interval;

std::string prefix = "local://" + FLAGS_data_path;

node_options.log_uri = prefix + "/log";

node_options.raft_meta_uri = prefix + "/raft_meta";

node_options.snapshot_uri = prefix + "/snapshot";

node_options.disable_cli = FLAGS_disable_cli;

braft::Node* node = new braft::Node(FLAGS_group, braft::PeerId(addr));

if (node->init(node_options) != 0) {

LOG(ERROR) << "Fail to init raft node";

delete node;

return -1;

}

_node = node;

return 0;

}initial_conf只有在这个复制组从空节点启动才会生效,当有snapshot和log里的数据不为空的时候的时候从其中恢复Configuration。initial_conf只用于创建复制组,第一个节点将自己设置进initial_conf,再调用add_peer添加其他节点,其他节点initial_conf设置为空;也可以多个节点同时设置相同的inital_conf(多个节点的ip:port)来同时启动空节点。

RAFT需要三种不同的持久存储, 分别是:RaftMetaStorage, 用来存放一些RAFT算法自身的状态数据, 比如term, vote_for等信息.

LogStorage, 用来存放用户提交的WAL

SnapshotStorage, 用来存放用户的Snapshot以及元信息. 用三个不同的uri来表示, 并且提供了基于本地文件系统的默认实现,type为local, 比如 local://data 就是存放到当前文件夹的data目录,local:///home/disk1/data 就是存放在 /home/disk1/data中。libraft中有默认的local://实现,用户可以根据需要继承实现相应的Storage。

1.4将操作提交到复制组

你需要将你的操作序列化成IOBuf, 这是一个非连续零拷贝的缓存结构。构造一

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值