第二章 多服务器分布式部署——服务器A和服务器B实现群组1的搭建

基础底层搭建

  • 目标

    本章的目标是用两个服务器,实现群组1的搭建,每个服务器各有两个节点。
    首先准备两台测试服务器:

  • 提示
    对服务器里的内容进行目录化的管理,可以使用WinSCP软件,并且本章中所有的命令行都是基于Ubuntu系统的。

在这里插入图片描述
在这里插入图片描述

下载安装

下载和安装在服务器A和服务器B都需要进行相同的操作。

  • 下载
cd ~/ && git clone https://github.com/FISCO-BCOS/generator.git
  • 安装
    此操作要求用户具有sudo权限。
cd ~/generator && bash ./scripts/install.sh

检查是否安装成功,若成功,会输出 usage: generator xxx

./generator -h
  • 获取节点二进制
    拉取最新fisco-bcos二进制文件到meta中
./generator --download_fisco ./meta
  • 检查二进制版本
    若成功,输出 FISCO-BCOS Version : x.x.x-x
./meta/fisco-bcos -v

初始化链证书

初始化链证书一个群组里只能在一个服务器上进行,这里采用的是在服务器A进行初始化链证书

cd ~/generator 
./generator --generate_chain_certificate ./dir_chain_ca

初始化机构A

在服务器A上进行初始化:

./generator --generate_agency_certificate ./dir_agency_ca ./dir_chain_ca agencyA

执行之后,会在 generator/dir_agency_ca/agencyA/目录中生成机钥证书和私钥:
在这里插入图片描述
将 generator/dir_agency_ca/agencyA/目录中的链证书、机构证书、机构私钥复制到 generator/meta/文件中

cp ./dir_agency_ca/agencyA/* ~/generator/meta/

复制后:
在这里插入图片描述

复制初始化链证书

因为只有服务器A生成了初始化链证书,这里需要手动复制服务器A上 generator/ 目录中的 dir_chain_ca,将A中的dir_chain_ca 复制到服务器B上的 generato/目录下。
在这里插入图片描述

初始化机构B

在服务器A上进行初始化:

./generator --generate_agency_certificate ./dir_agency_ca ./dir_chain_ca agencyB

在这里插入图片描述
执行之后,会在 generator/dir_agency_ca/agencyB/目录中生成机钥证书和私钥。
将 generator/dir_agency_ca/agencyB/目录中的链证书、机构证书、机构私钥复制到 generator/meta/文件中

cp ./dir_agency_ca/agencyB/* ~/generator/meta/

复制后:
在这里插入图片描述

修改配置文件

手动的方式修改conf文件夹下面node_deployment.ini
p2p_ip地址修改为该服务器对应的外网ip,其中rpc_ip要修改为内网ip

在这里插入图片描述
上图为服务器A中的node_deployment.ini配置。服务器B中的node_deployment.i也根据服务器ip进行相应的修改

机构A生成并发送节点信息

机构(服务器)A生成并发送节点信息

./generator --generate_all_certificates ./agencyA_node_info

在这里插入图片描述
机构B生成并发送节点信息

./generator --generate_all_certificates ./agencyB_node_info

在这里插入图片描述

创世机构收集节点证书

生成创世区块的机构需要节点证书,本章中是由A机构生成创世区块,因此B机构需要发送节点证书至机构A。将机构B节点agencyB_node_info文件夹cert_*.crt文件copy到机构A的meta文件夹中。

在这里插入图片描述
上图是将机构B中的两个节点证书成功拷贝到机构A中的meta,此时meta中共有四个节点证书,两个为机构A的,两个为机构B的。

各机构节点连接信息相互收集

例如:将机构B中的 peersB.txt 拷贝到机构A的meta文件夹,同时,将机构A中的 peersA.txt 拷贝到机构B的meta文件夹。每个peers*.txt里都是该机构的节点连接信息。
在这里插入图片描述
在这里插入图片描述

注:在机构B中,一开始txt的名称为peers.txt,需要将其复制重命名为peerB.txt,然后拷贝到机构A中。机构A中也同理。上面两张图中都有peers.txt,这是多余的,大家在实际操作中是没有的。

机构A生成群组1创世区块

  • 将机构A中的generator/conf/conf/group_genesis.ini进行手动修改,配置成如图:
    在这里插入图片描述
    其中node0-node3这四个节点,分别为两个服务器上节点的外网ip+端口

  • 执行命令 生成group_genesis.ini 配置的群组创世区块:

./generator --create_group_genesis ./group

在这里插入图片描述

  • 分发群组1创世区块至机构B
    手动复制 机构A中的 ./group/group.1.genesis文件到机构B的meta文件夹下。
    在这里插入图片描述

机构A生成所属节点

执行:

./generator --build_install_package ./meta/peersB.txt ./nodeA

在这里插入图片描述

机构B生成所属节点

执行:

./generator --build_install_package ./meta/peersA.txt ./nodeB

在这里插入图片描述

各自启动机构下的节点

  • 启动A机构下的节点:
bash ./nodeA/start_all.sh

在这里插入图片描述

  • 启动B机构下的节点:
bash ./nodeB/start_all.sh

在这里插入图片描述

注意:如果端口被占用,可以根据端口查找进程

sudo lsof -i:[port]

然后根据PID杀掉进程

sudo kill [PID]

控制台部署

在服务器A和服务器B都进行以下的配置。

  • 添加ppa源
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update

如果报错:则

add-apt-repository
sudo apt-get install python-software-properties

然后再:

sudo add-apt-repository ppa:webupd8team/java
  • 安装openjdk
sudo apt install openjdk-8-jre-headless
  • 配置jdk
    下载jkd到电脑上:链接:下载jdk网址
    新建software文件夹,将jdk压缩包放置其中
    在/usr/lib/ 新建jdk文件夹,在software文件里执行解压命令
sudo tar -zxvf jdk-8u231-linux-x64.tar.gz -C /usr/lib/jdk
  • 修改配置
sudo vi /etc/profile
export JAVA_HOME=/usr/lib/jdk/jdk1.8.0_231
export JRE_HOME=${JAVA_HOME}/jre
export PATH=${JAVA_HOME}/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:${JRE_HOME}/lib

执行命令,立即生效:

source /etc/profile

sudo update-alternatives --install /usr/bin/java java /usr/lib/jdk/jdk1.8.0_231/bin/java 300
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jdk/jdk1.8.0_231/bin/javac 300
  • 下载控制台程序
./generator --download_console ./ --cdn

在这里插入图片描述

  • 在服务器A中执行
./generator --generate_sdk_certificate ./dir_sdk_ca ./dir_agency_ca/agencyA
  • 在服务B中执行
./generator --generate_sdk_certificate ./dir_sdk_ca ./dir_agency_ca/agencyB

以上所有操作执行完,便完成了控制台的部署。

示例

进入generator/console 后,输入:

./start.sh

即可进入控制台。
可以部署简单的Asset.sol合约,合约代码:

pragma solidity ^0.4.24;

import "./Table.sol";

contract Asset {
    // event
    event RegisterEvent(int256 ret, string account, uint256 asset_value);
    event TransferEvent(int256 ret, string from_account, string to_account, uint256 amount);
    
    constructor() public {
        // 构造函数中创建t_asset表
        createTable();
    }

    function createTable() private {
        TableFactory tf = TableFactory(0x1001); 
        // 资产管理表, key : account, field : asset_value
        // |  资产账户(主键)      |     资产金额       |
        // |-------------------- |-------------------|
        // |        account      |    asset_value    |     
        // |---------------------|-------------------|
        //
        // 创建表
        tf.createTable("t_asset", "account", "asset_value");
    }

    function openTable() private returns(Table) {
        TableFactory tf = TableFactory(0x1001);
        Table table = tf.openTable("t_asset");
        return table;
    }

    /*
    描述 : 根据资产账户查询资产金额
    参数 : 
            account : 资产账户

    返回值:
            参数一: 成功返回0, 账户不存在返回-1
            参数二: 第一个参数为0时有效,资产金额
    */
    function select(string account) public constant returns(int256, uint256) {
        // 打开表
        Table table = openTable();
        // 查询
        Entries entries = table.select(account, table.newCondition());
        uint256 asset_value = 0;
        if (0 == uint256(entries.size())) {
            return (-1, asset_value);
        } else {
            Entry entry = entries.get(0);
            return (0, uint256(entry.getInt("asset_value")));
        }
    }

    /*
    描述 : 资产注册
    参数 : 
            account : 资产账户
            amount  : 资产金额
    返回值:
            0  资产注册成功
            -1 资产账户已存在
            -2 其他错误
    */
    function register(string account, uint256 asset_value) public returns(int256){
        int256 ret_code = 0;
        int256 ret= 0;
        uint256 temp_asset_value = 0;
        // 查询账户是否存在
        (ret, temp_asset_value) = select(account);
        if(ret != 0) {
            Table table = openTable();
            
            Entry entry = table.newEntry();
            entry.set("account", account);
            entry.set("asset_value", int256(asset_value));
            // 插入
            int count = table.insert(account, entry);
            if (count == 1) {
                // 成功
                ret_code = 0;
            } else {
                // 失败? 无权限或者其他错误
                ret_code = -2;
            }
        } else {
            // 账户已存在
            ret_code = -1;
        }

        emit RegisterEvent(ret_code, account, asset_value);

        return ret_code;
    }

    /*
    描述 : 资产转移
    参数 : 
            from_account : 转移资产账户
            to_account : 接收资产账户
            amount : 转移金额
    返回值:
            0  资产转移成功
            -1 转移资产账户不存在
            -2 接收资产账户不存在
            -3 金额不足
            -4 金额溢出
            -5 其他错误
    */
    function transfer(string from_account, string to_account, uint256 amount) public returns(int256) {
        // 查询转移资产账户信息
        int ret_code = 0;
        int256 ret = 0;
        uint256 from_asset_value = 0;
        uint256 to_asset_value = 0;
        
        // 转移账户是否存在?
        (ret, from_asset_value) = select(from_account);
        if(ret != 0) {
            ret_code = -1;
            // 转移账户不存在
            emit TransferEvent(ret_code, from_account, to_account, amount);
            return ret_code;

        }

        // 接受账户是否存在?
        (ret, to_asset_value) = select(to_account);
        if(ret != 0) {
            ret_code = -2;
            // 接收资产的账户不存在
            emit TransferEvent(ret_code, from_account, to_account, amount);
            return ret_code;
        }

        if(from_asset_value < amount) {
            ret_code = -3;
            // 转移资产的账户金额不足
            emit TransferEvent(ret_code, from_account, to_account, amount);
            return ret_code;
        } 

        if (to_asset_value + amount < to_asset_value) {
            ret_code = -4;
            // 接收账户金额溢出
            emit TransferEvent(ret_code, from_account, to_account, amount);
            return ret_code;
        }

        Table table = openTable();

        Entry entry0 = table.newEntry();
        entry0.set("account", from_account);
        entry0.set("asset_value", int256(from_asset_value - amount));
        // 更新转账账户
        int count = table.update(from_account, entry0, table.newCondition());
        if(count != 1) {
            ret_code = -5;
            // 失败? 无权限或者其他错误?
            emit TransferEvent(ret_code, from_account, to_account, amount);
            return ret_code;
        }

        Entry entry1 = table.newEntry();
        entry1.set("account", to_account);
        entry1.set("asset_value", int256(to_asset_value + amount));
        // 更新接收账户
        table.update(to_account, entry1, table.newCondition());

        emit TransferEvent(ret_code, from_account, to_account, amount);

        return ret_code;
    }
}

效果图:
在这里插入图片描述
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值