溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java持久化和命令行怎么用

發(fā)布時(shí)間:2022-01-06 16:55:30 來(lái)源:億速云 閱讀:151 作者:iii 欄目:互聯(lián)網(wǎng)科技

這篇文章主要講解了“Java持久化和命令行怎么用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java持久化和命令行怎么用”吧!

數(shù)據(jù)庫(kù)選擇

我們這里使用的是Java來(lái)實(shí)現(xiàn),BoltDB不支持Java,這里我們選用 Rocksdb 。

數(shù)據(jù)結(jié)構(gòu)

在我們開(kāi)始實(shí)現(xiàn)數(shù)據(jù)持久化之前,我們先要確定我們?cè)撊绾稳ゴ鎯?chǔ)我們的數(shù)據(jù)。為此,我們先來(lái)看看比特幣是怎么做的。

簡(jiǎn)單來(lái)講,比特幣使用了兩個(gè)"buckets(桶)"來(lái)存儲(chǔ)數(shù)據(jù):

  • blocks. 描述鏈上所有區(qū)塊的元數(shù)據(jù).

  • chainstate. 存儲(chǔ)區(qū)塊鏈的狀態(tài),指的是當(dāng)前所有的UTXO(未花費(fèi)交易輸出)以及一些元數(shù)據(jù).

“在比特幣的世界里既沒(méi)有賬戶,也沒(méi)有余額,只有分散到區(qū)塊鏈里的UTXO?!?/p>

詳見(jiàn):《精通比特幣》第二版 第06章節(jié) —— 交易的輸入與輸出

此外,每個(gè)區(qū)塊數(shù)據(jù)都是以單獨(dú)的文件形式存儲(chǔ)在磁盤上。這樣做是出于性能的考慮:當(dāng)讀取某一個(gè)單獨(dú)的區(qū)塊數(shù)據(jù)時(shí),不需要加載所有的區(qū)塊數(shù)據(jù)到內(nèi)存中來(lái)。

blocks 這個(gè)桶中,存儲(chǔ)的鍵值對(duì):

  • 'b' + 32-byte block hash -> block index record

    區(qū)塊的索引記錄

  • 'f' + 4-byte file number -> file information record

    文件信息記錄

  • 'l' -> 4-byte file number: the last block file number used

    最新的一個(gè)區(qū)塊所使用的文件編碼

  • 'R' -> 1-byte boolean: whether we're in the process of reindexing

    是否處于重建索引的進(jìn)程當(dāng)中

  • 'F' + 1-byte flag name length + flag name string -> 1 byte boolean: various flags that can be on or off

    各種可以打開(kāi)或關(guān)閉的flag標(biāo)志

  • 't' + 32-byte transaction hash -> transaction index record

    交易索引記錄

chainstate 這個(gè)桶中,存儲(chǔ)的鍵值對(duì):

  • 'c' + 32-byte transaction hash -> unspent transaction output record for that transaction

    某筆交易的UTXO記錄

  • 'B' -> 32-byte block hash: the block hash up to which the database represents the unspent transaction outputs

    數(shù)據(jù)庫(kù)所表示的UTXO的區(qū)塊Hash(抱歉,這一點(diǎn)我還沒(méi)弄明白……)

由于我們還沒(méi)有實(shí)現(xiàn)交易相關(guān)的特性,因此,我們這里只使用 block 桶。另外,前面提到過(guò)的,這里我們不會(huì)實(shí)現(xiàn)各個(gè)區(qū)塊數(shù)據(jù)各自存儲(chǔ)在獨(dú)立的文件上,而是統(tǒng)一存放在一個(gè)文件里面。因此,我們不要存儲(chǔ)和文件編碼相關(guān)的數(shù)據(jù),這樣一來(lái),我們所用到的鍵值對(duì)就簡(jiǎn)化為:

  • 32-byte block-hash -> Block structure (serialized)

    區(qū)塊數(shù)據(jù)與區(qū)塊hash的鍵值對(duì)

  • 'l' -> the hash of the last block in a chain

    最新一個(gè)區(qū)塊hash的鍵值對(duì)

(查看更加詳細(xì)的解釋)

序列化

RocksDB的Key與Value只能以byte[]的形式進(jìn)行存儲(chǔ),這里我們需要用到序列化與反序列化庫(kù) Kryo,代碼如下:

package one.wangwei.blockchain.util;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

/**
 * 序列化工具類
 *
 * @author wangwei
 * @date 2018/02/07
 */
public class SerializeUtils {

    /**
     * 反序列化
     *
     * @param bytes 對(duì)象對(duì)應(yīng)的字節(jié)數(shù)組
     * @return
     */
    public static Object deserialize(byte[] bytes) {
        Input input = new Input(bytes);
        Object obj = new Kryo().readClassAndObject(input);
        input.close();
        return obj;
    }

    /**
     * 序列化
     *
     * @param object 需要序列化的對(duì)象
     * @return
     */
    public static byte[] serialize(Object object) {
        Output output = new Output(4096, -1);
        new Kryo().writeClassAndObject(output, object);
        byte[] bytes = output.toBytes();
        output.close();
        return bytes;
    }
}

持久化

上面已經(jīng)說(shuō)過(guò),我們這里使用RocksDB,我們先寫(xiě)一個(gè)相關(guān)的工具類RocksDBUtils,主要的功能如下:

  • putLastBlockHash:保存最新一個(gè)區(qū)塊的Hash值

  • getLastBlockHash:查詢最新一個(gè)區(qū)塊的Hash值

  • putBlock:保存區(qū)塊

  • getBlock:查詢區(qū)塊

注意:BoltDB 支持 Bucket 的特性,而RocksDB 不支持,所以需要我們自己使用Map來(lái)做一個(gè)映射。

RocksDBUtils

package one.wangwei.blockchain.store;

import com.google.common.collect.Maps;
import one.wangwei.blockchain.block.Block;
import one.wangwei.blockchain.util.SerializeUtils;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;

import java.util.Map;

/**
 * 存儲(chǔ)工具類
 *
 * @author wangwei
 * @date 2018/02/27
 */
public class RocksDBUtils {

    /**
     * 區(qū)塊鏈數(shù)據(jù)文件
     */
    private static final String DB_FILE = "blockchain.db";
    /**
     * 區(qū)塊桶前綴
     */
    private static final String BLOCKS_BUCKET_KEY = "blocks";
    /**
     * 最新一個(gè)區(qū)塊
     */
    private static final String LAST_BLOCK_KEY = "l";

    private volatile static RocksDBUtils instance;

    public static RocksDBUtils getInstance() {
        if (instance == null) {
            synchronized (RocksDBUtils.class) {
                if (instance == null) {
                    instance = new RocksDBUtils();
                }
            }
        }
        return instance;
    }

    private RocksDB db;

    /**
     * block buckets
     */
    private Map<String, byte[]> blocksBucket;

    private RocksDBUtils() {
        openDB();
        initBlockBucket();
    }

    /**
     * 打開(kāi)數(shù)據(jù)庫(kù)
     */
    private void openDB() {
        try {
            db = RocksDB.open(DB_FILE);
        } catch (RocksDBException e) {
            throw new RuntimeException("Fail to open db ! ", e);
        }
    }

    /**
     * 初始化 blocks 數(shù)據(jù)桶
     */
    private void initBlockBucket() {
        try {
            byte[] blockBucketKey = SerializeUtils.serialize(BLOCKS_BUCKET_KEY);
            byte[] blockBucketBytes = db.get(blockBucketKey);
            if (blockBucketBytes != null) {
                blocksBucket = (Map) SerializeUtils.deserialize(blockBucketBytes);
            } else {
                blocksBucket = Maps.newHashMap();
                db.put(blockBucketKey, SerializeUtils.serialize(blocksBucket));
            }
        } catch (RocksDBException e) {
            throw new RuntimeException("Fail to init block bucket ! ", e);
        }
    }

    /**
     * 保存最新一個(gè)區(qū)塊的Hash值
     *
     * @param tipBlockHash
     */
    public void putLastBlockHash(String tipBlockHash) {
        try {
            blocksBucket.put(LAST_BLOCK_KEY, SerializeUtils.serialize(tipBlockHash));
            db.put(SerializeUtils.serialize(BLOCKS_BUCKET_KEY), SerializeUtils.serialize(blocksBucket));
        } catch (RocksDBException e) {
            throw new RuntimeException("Fail to put last block hash ! ", e);
        }
    }

    /**
     * 查詢最新一個(gè)區(qū)塊的Hash值
     *
     * @return
     */
    public String getLastBlockHash() {
        byte[] lastBlockHashBytes = blocksBucket.get(LAST_BLOCK_KEY);
        if (lastBlockHashBytes != null) {
            return (String) SerializeUtils.deserialize(lastBlockHashBytes);
        }
        return "";
    }

    /**
     * 保存區(qū)塊
     *
     * @param block
     */
    public void putBlock(Block block) {
        try {
            blocksBucket.put(block.getHash(), SerializeUtils.serialize(block));
            db.put(SerializeUtils.serialize(BLOCKS_BUCKET_KEY), SerializeUtils.serialize(blocksBucket));
        } catch (RocksDBException e) {
            throw new RuntimeException("Fail to put block ! ", e);
        }
    }

    /**
     * 查詢區(qū)塊
     *
     * @param blockHash
     * @return
     */
    public Block getBlock(String blockHash) {
        return (Block) SerializeUtils.deserialize(blocksBucket.get(blockHash));
    }

    /**
     * 關(guān)閉數(shù)據(jù)庫(kù)
     */
    public void closeDB() {
        try {
            db.close();
        } catch (Exception e) {
            throw new RuntimeException("Fail to close db ! ", e);
        }
    }
}

創(chuàng)建區(qū)塊鏈

現(xiàn)在我們來(lái)優(yōu)化 Blockchain.newBlockchain 接口的代碼邏輯,改為如下邏輯:

Java持久化和命令行怎么用

代碼如下:

/**
  * <p> 創(chuàng)建區(qū)塊鏈 </p>
  *
  * @return
  */
public static Blockchain newBlockchain() throws Exception {
    String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash();
    if (StringUtils.isBlank(lastBlockHash)) {
        Block genesisBlock = Block.newGenesisBlock();
        lastBlockHash = genesisBlock.getHash();
        RocksDBUtils.getInstance().putBlock(genesisBlock);
        RocksDBUtils.getInstance().putLastBlockHash(lastBlockHash);
     }
     return new Blockchain(lastBlockHash);
}

修改 Blockchain 的數(shù)據(jù)結(jié)構(gòu),只記錄最新一個(gè)區(qū)塊鏈的Hash值

public class Blockchain {
    
    @Getter
    private String lastBlockHash;

    private Blockchain(String lastBlockHash) {
        this.lastBlockHash = lastBlockHash;
    }
}

每次挖礦完成后,我們也需要將最新的區(qū)塊信息保存下來(lái),并且更新最新區(qū)塊鏈Hash值:

/**
 * <p> 添加區(qū)塊  </p>
 *
 * @param data
 */
public void addBlock(String data) throws Exception {
   String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash();
   if (StringUtils.isBlank(lastBlockHash)) {
       throw new Exception("Fail to add block into blockchain ! ");
   }
   this.addBlock(Block.newBlock(lastBlockHash, data));
}

/**
 * <p> 添加區(qū)塊  </p>
 *
 * @param block
 */
public void addBlock(Block block) throws Exception {
    RocksDBUtils.getInstance().putLastBlockHash(block.getHash());
    RocksDBUtils.getInstance().putBlock(block);
    this.lastBlockHash = block.getHash();
}

到此,存儲(chǔ)部分的功能就實(shí)現(xiàn)完畢,我們還缺少一個(gè)功能:

檢索區(qū)塊鏈

現(xiàn)在,我們所有的區(qū)塊都保存到了數(shù)據(jù)庫(kù),因此,我們能夠重新打開(kāi)已有的區(qū)塊鏈并且向其添加新的區(qū)塊。但這也導(dǎo)致我們?cè)僖矡o(wú)法打印出區(qū)塊鏈中所有區(qū)塊的信息,因?yàn)椋覀儧](méi)有將區(qū)塊存儲(chǔ)在數(shù)組當(dāng)中。讓我們來(lái)修復(fù)這個(gè)瑕疵!

我們?cè)贐lockchain中創(chuàng)建一個(gè)內(nèi)部類 BlockchainIterator ,作為區(qū)塊鏈的迭代器,通過(guò)區(qū)塊之前的hash連接來(lái)依次迭代輸出區(qū)塊信息,代碼如下:

public class Blockchain {
 
    ....
    
    /**
     * 區(qū)塊鏈迭代器
     */
    public class BlockchainIterator {

        private String currentBlockHash;

        public BlockchainIterator(String currentBlockHash) {
            this.currentBlockHash = currentBlockHash;
        }

        /**
         * 是否有下一個(gè)區(qū)塊
         *
         * @return
         */
        public boolean hashNext() throws Exception {
            if (StringUtils.isBlank(currentBlockHash)) {
                return false;
            }
            Block lastBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);
            if (lastBlock == null) {
                return false;
            }
            // 創(chuàng)世區(qū)塊直接放行
            if (lastBlock.getPrevBlockHash().length() == 0) {
                return true;
            }
            return RocksDBUtils.getInstance().getBlock(lastBlock.getPrevBlockHash()) != null;
        }

        
        /**
         * 返回區(qū)塊
         *
         * @return
         */
        public Block next() throws Exception {
            Block currentBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);
            if (currentBlock != null) {
                this.currentBlockHash = currentBlock.getPrevBlockHash();
                return currentBlock;
            }
            return null;
        }
    }   
    
    ....    
}

測(cè)試

/**
 * 測(cè)試
 *
 * @author wangwei
 * @date 2018/02/05
 */
public class BlockchainTest {

    public static void main(String[] args) {
        try {
            Blockchain blockchain = Blockchain.newBlockchain();

            blockchain.addBlock("Send 1.0 BTC to wangwei");
            blockchain.addBlock("Send 2.5 more BTC to wangwei");
            blockchain.addBlock("Send 3.5 more BTC to wangwei");

            for (Blockchain.BlockchainIterator iterator = blockchain.getBlockchainIterator(); iterator.hashNext(); ) {
                Block block = iterator.next();

                if (block != null) {
                    boolean validate = ProofOfWork.newProofOfWork(block).validate();
                    System.out.println(block.toString() + ", validate = " + validate);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/*輸出*/

Block{hash='0000012f87a0510dd0ee7048a6bd52db3002bae7d661126dc28287bd6c23189a', prevBlockHash='0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf', data='Send 3.5 more BTC to wangwei', timeStamp=1519724875, nonce=369110}, validate = true
Block{hash='0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf', prevBlockHash='00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79', data='Send 2.5 more BTC to wangwei', timeStamp=1519724872, nonce=896348}, validate = true
Block{hash='00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79', prevBlockHash='0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703', data='Send 1.0 BTC to wangwei', timeStamp=1519724869, nonce=673955}, validate = true
Block{hash='0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703', prevBlockHash='', data='Genesis Block', timeStamp=1519724866, nonce=840247}, validate = true

命令行界面

CLI 部分的內(nèi)容,這里不做詳細(xì)介紹,具體可以去查看文末的Github源碼鏈接。大致步驟如下:

配置

添加pom.xml配置

<project>
   
    ...
    
	<dependency>
		<groupId>commons-cli</groupId>
		<artifactId>commons-cli</artifactId>
		<version>1.4</version>
	</dependency>
    
    ...
    
	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-assembly-plugin</artifactId>
		<version>3.1.0</version>
		<configuration>
			<archive>
				<manifest>
					<addClasspath>true</addClasspath>
					<classpathPrefix>lib/</classpathPrefix>
					<mainClass>one.wangwei.blockchain.cli.Main</mainClass>
				</manifest>
			</archive>
			<descriptorRefs>
				<descriptorRef>jar-with-dependencies</descriptorRef>
			</descriptorRefs>
		</configuration>
		<executions>
			<execution>
				<id>make-assembly</id>
				<!-- this is used for inheritance merges -->
				<phase>package</phase>
				<!-- 指定在打包節(jié)點(diǎn)執(zhí)行jar包合并操作 -->
				<goals>
					<goal>single</goal>
				</goals>
			</execution>
		</executions>
	</plugin>
    
    ...
   
</project>
項(xiàng)目工程打包
$ mvn clean && mvn package
執(zhí)行命令
# 打印幫助信息
$ java -jar blockchain-java-jar-with-dependencies.jar -h 

# 添加區(qū)塊
$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 1.5 BTC to wangwei"
$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 2.5 BTC to wangwei"
$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 3.5 BTC to wangwei"

# 打印區(qū)塊鏈
$ java -jar blockchain-java-jar-with-dependencies.jar -print

感謝各位的閱讀,以上就是“Java持久化和命令行怎么用”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java持久化和命令行怎么用這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI