BFT-SMaRt:用Java做节点间的可靠信道

本文深入解析BFT-SMaRt如何利用Java构建节点间的可靠信道,重点关注SSL/TLS协议在节点通信中的应用。通过跟踪源码,文章详细阐述了节点服务类、节点通信系统概览、节点通信层的建立和重连机制,展示了如何确保节点间安全的Socket IO连接。
摘要由CSDN通过智能技术生成

关键字:区块链 可靠信道 BFT-SMaRt Socket SSL/TLS 网络通信

信道的可靠是BFT的前提(参见两军问题)

本文通过跟踪BFT-SMaRt通信层源码,研究节点间可靠信道的实现原理。本文涉及区块链方面的内容较少,重点研究使用Java语言建立可靠网络通道的技术,请选择性阅读。

通信层系统,是分布式网络中获得可靠且认证的点对点通道的保证。BFT-SMaRt的安全通信是基于SSL/TLS标准。

  • 节点之间建立互为信任的Socket IO连接,实现点对点的消息处理。
  • 节点与客户端之间建立健壮性、可用性更高的Netty NIO连接,实现大规模的消息处理。

本文主要介绍第一种情况:在BFT-SMaRt中,作为服务端的节点之间的连接构建方法。

一、引子

接上一篇 BFT-SMaRt的理论与实践 ,启动分布式计数器服务示例程序时,需输入命令:

runscripts/smartrun.sh bftsmart.demo.counter.CounterServer 0

命令调用的是CounterServer类的内容,先查看CounterServer的类结构。

接着再看一下CounterServer的类图。

通过CounterServer的类图可以清晰地展示它的关系结构。其父类DefaultSingleRecoverable实现了Recoverable和SingleExecutable接口,而SingleExecutable继承了Executable。追本溯源,根部是以下两个接口:

  • Recoverable:恢复程序,实现此接口的类应该实现状态转移协议。通常,类应该同时实现这个接口和一个Executable。
  • Executable:执行程序,实现此接口,可接收无序的客户端请求。如果要支持有序的请求,可以选择其子类接口FIFOExecutable、BatchExecutable或SingleExecutable。

回到CounterServer的源码,它是所有官方示例中架构最简单的,这会提高我们的学习效率。CounterServer有两个类属性:

  1. counter,计数字段,保存计数器的状态值。
  2. iterations,操作次数,日志记录以及数据恢复时使用。

下面开始项目调试,我们为命令手动配置添加请求参数“0”作为节点id,然后启动命令进入CounterServer的main入口函数:

public static void main(String[] args){
    if(args.length < 1) {
        System.out.println("Use: java CounterServer <processId>");
        System.exit(-1);
    }      
    new CounterServer(Integer.parseInt(args[0]));
}

进来先校验参数个数,然后调用CounterServer构造函数。函数内创建了一个ServiceReplica对象。

public CounterServer(int id) {
    new ServiceReplica(id, this, this);
}

传入的第一个参数是命令带入的唯一参数,即节点id。后面两个参数的值都是this,是将CounterServer分别作为执行程序和恢复程序。

二、名词统一

在本文的研究中,会涉及到一些由本系统提出,并且非常重要的名词概念。为避免后续发生同一件事的称呼混乱,造成困扰,在这里统一声明。

1. 节点id

replicaId、processId、remoteId、TTPid指的都是节点id,但包含以下几种情况:

  • replicaId:节点作为一个副本的时候。
  • processId:一个处理单元作为节点的时候。
  • remoteId:外部的节点id。
  • TTPid:设置的TTP节点的id。

2. 节点

Replica是分布式系统中的副本,在区块链网络中代表一个服务节点,节点不一定是一台机器,也可能是一个处理单元,下面统一称作节点。注意,所有节点都是用一套代码编译部署的环境。

3. 本地节点

本文依据命令runscripts/smartrun.sh bftsmart.demo.counter.CounterServer 0,因此本地节点都指的是CounterServer,id为0的节点。本文只研究本地作为节点的情况。而本地作为客户端的情况(CounterClient作为入口),在后续文章介绍。

4. 配置域

组网配置文件host.config所描述的,由确定数量且顺序编号的节点所组成的网络,我们可以称之为配置域。下面就是示例节点的host.config的内容。

#server id, address and port (the ids from 0 to n-1 are the service replicas) 
0 127.0.0.1 11000 11001
1 127.0.0.1 11010 11011
2 127.0.0.1 11020 11021
3 127.0.0.1 11030 11031

5. TTP

在system.config系统配置文件中有相关配置项:

system.ttp.id

该配置项的值是一个节点id,所以只能配置一个,我们称之为TTPid。一般是在配置域外,可用作向系统添加和删除节点。注意,根据约定,TTPid一定大于配置域任意id。

6. 陌生域

那么如果一个节点id既不属于配置域,又不是ttpid,我们称之为陌生id。除配置域和ttp以外的所有空间,我们称之为陌生域。陌生id的加入,在主流区块链产品例如比特币等,都会有完整的解决方案。BFT-SMaRt的分布式网络中除了启动时的配置域和TTP,也允许陌生id的接入,后面会有相关介绍。换句话讲,P2P网络最精彩的部分就是与陌生域的自由联系。当然了,如果是成熟的联盟链产品,会通过权限控制管理配置域、TTP和陌生域。


三、节点服务类

进入ServiceReplica的构造函数就意味着离开了示例程序,深入到了BFT-SMaRt标准库内容。ServiceReplica类可以被称为本地节点服务类,主要用作管理本地作为节点的基础服务,包括网络通信和节点间消息共识。这个类从DeliveryThread接收消息,并管理应用程序的执行和对客户端的回复。对于顺序消息逐个执行的应用程序,ServiceReplica接收一致决定的批处理,逐个交付,并使用批处理回复。在应用程序成批执行消息的情况下,该批消息被交付给应用程序,ServiceReplica不需要成批组织应答。

/**
 * Constructor
 *
 * @param id            节点(副本)ID
 * @param configHome    配置文件
 * @param executor      执行器
 * @param recoverer     恢复器
 * @param verifier      请求校验器
 * @param replier       请求回复器
 * @param loader        加载签名器
 */
public ServiceReplica(int id, String configHome, Executable executor, Recoverable recoverer, RequestVerifier verifier, Replier replier, KeyLoader loader) {
    this.id = id;
    // 读取配置文件,构建配置域视图实例。
    this.SVController = new ServerViewController(id, configHome, loader); // NEXT TODO
    this.executor = executor;   // 传入执行程序
    this.recoverer = recoverer; // 传入恢复程序
    this.replier = (replier != null ? replier : new DefaultReplier()); // 回复的包装类
    this.verifier = verifier;   // null,判断请求有效性,只是true/flase,未展开有效判断的逻辑。
    this.init();                // 节点初始化(重点)
    // 节点环境,上下文内容,属共识层内容,在恢复程序和回复消息时都被需要。
    this.recoverer.setReplicaContext(replicaCtx);   // NEXT TODO
    this.replier.setReplicaContext(replicaCtx);     // NEXT TODO
}

回复器接口Replier只有一个实现类DefaultReplier。它的功能就是在正常的回复开始前,保证对节点上下文ReplicaContext的校验。当ReplicaContext为空时,会挂起等待,直到有值时,才会走正常的回复。 ServerViewController是共识层的内容,本篇不展开。接下来,进入执行init初始化函数。

private void init() {
    try {
        cs = new ServerCommunicationSystem(this.SVController, this); // 创建本地节点通信系统
    } catch (Exception ex) {
        logger.error("Failed to initialize replica-to-replica communication system", ex);
        throw new RuntimeException("Unable to build a communication system.");
    }
    if (this.SVController.isInCurrentView()) {
        logger.info("In curre
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值