hyperledger fabric1.0整体架构与记账逻辑架构的分析

hyperledger fabric1.0整体架构与记账逻辑架构的分析

大家好,又见面了,我是全栈君。

1、关于逻辑架构的一些问题

1)CLI客户端和peer节点之间是如何沟通的?

2)Peer节点之间如何数据传输处理,与cli和peer之间的方式有何不同?

3)数据何时进入orderer节点,orderer节点是如何处理的?(0.6里面就是共识这块怎么处理)

4)链码(CC)是如何与节点或cli或rest api交互的,怎么实现的?

2、新旧架构的比较

旧版本(0.6)的运行时架构:

135313_AydD_568367.png

新版本(1.0)的运行时架构:

135354_OHnw_568367.png

3、fabric1.0记账的逻辑分析

Fabric账本逻辑架构

135421_0sF3_568367.png

Fabric 1.0中的账本分为3种:

区块链数据,这是用文件系统存储在Committer节点上的。区块链中存储了Transaction的读写集。

为了检索区块链的方便,所以用LevelDB对其中的Transaction进行了索引。

ChainCode操作的实际数据存储在State Database中,这是一个Key Value的数据库,默认采用的LevelDB,现在1.0也支持使用CouchDB作为State Database。

当执行a向b转账10元,我们在cli中执行的命令为:

peer chaincode invoke -o orderer.example.com:7050  --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/cacerts/ca.example.com-cert.pem  -C mychannel -n devincc -c '{"Args":["invoke","a","b","10"]}'

 这个过程是这样的:

135517_b737_568367.png

其中peer chaincode invoke表明这是一个Transaction调用。-c ‘{“Args”:[“invoke”,”a”,”b”,”10″]}’中的”invoke”说明调用的是example02.go中的invoke函数,具体函数我们可以看看到底实现了什么功能:

// Transaction makes payment of X units from A to B

func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A, B string // Entities
var Aval, Bval int // Asset holdings
var X int // Transaction value
var err error
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3")
    }
A = args[0]
B = args[1]
// Get the state from the ledger
// TODO: will be nice to have a GetAllState call to ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
return shim.Error("Failed to get state")
    }
if Avalbytes == nil {
return shim.Error("Entity not found")
    }
Aval, _ = strconv.Atoi(string(Avalbytes))
Bvalbytes, err := stub.GetState(B)
if err != nil {
return shim.Error("Failed to get state")
    }

if Bvalbytes == nil {
return shim.Error("Entity not found")
    }

Bval, _ = strconv.Atoi(string(Bvalbytes))
// Perform the execution
X, err = strconv.Atoi(args[2])
if err != nil {
return shim.Error("Invalid transaction amount, expecting a integer value")
    }
Aval = Aval - X
Bval = Bval + X
    fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state back to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
    }
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
    }
return shim.Success(nil)
}

其中主要的4个关于StateDatabase调用是:

Avalbytes, err := stub.GetState(A)
Bvalbytes, err := stub.GetState(B)
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))

1.客户端SDK把'{“Args”:[“invoke”,”a”,”b”,”10″]}’这些参数发送到endorser peer节点,
2.endorser peer会与ChainCode的docker实例通信,并为其提供模拟的State Database的读写集,也就是说ChainCode会执行完逻辑,但是并不会在stub.PutState的时候写数据库。
3.endorser把这些读写集连同签名返回给Client SDK。
4.SDK再把读写集发送给Orderer节点,Orderer节点是进行共识的排序节点,在测试的情况下,只启动一个orderer节点,没有容错。在生产环境,要进行Crash容错,需要启用Zookeeper和Kafka。在1.0中移除了拜占庭容错,没有0.6的PBFT,也没有传说中的SBFT,不得不说是一个遗憾。
5.Orderer节点只是负责排序和打包工作,处理的结果是一个Batch的Transactions,也就是一个Block,这个Block的产生有两种情况,一种情况是Transaction很多,Block的大小达到了设定的大小,而另一种情况是Transaction很少,没有达到设定的大小,那么Orderer就会等,等到大小足够大或者超时时间。这些设置是在configtx.yaml中设定的。
6.打包好的一堆Transactions会发送给Committer Peer提交节点,
7.提交节点收到Orderer节点的数据后,会先进行VSCC校验,检查Block的数据是否正确。接下来是对每个Transaction的验证,主要是验证Transaction中的读写数据集是否与State Database的数据版本一致。验证完Block中的所有Transactions后,提交节点会把吧Block写入区块链。然后把所有验证通过的Transaction的读写集中的写的部分写入State Database。另外对于区块链,本身是文件系统,不是数据库,所有也会有把区块中的数据在LevelDB中建立索引。

 

查询a账户的cli命令是:

peer chaincode query -C mychannel -n devincc -c '{"Args":["query","a"]}'

这样系统会调用ChainCode中的invoke函数,但是传入的function name是query。也就是会执行如下代码:

} else if function == "query" {
// the old "Query" is now implemtned in invoke
return t.query(stub, args)
}
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A string // Entities
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
}
A = args[0]
// Get the state from the ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return shim.Error(jsonResp)
}

if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return shim.Error(jsonResp)
}
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp)
return shim.Success(Avalbytes)
}

我们可以看到,我们只是调用了stub.GetState(A),并没有写操作,那么会像前面说的Transaction一样那么复杂吗?答案是不会。因为调用调用的是peer query,在代码中,只有invoke的时候才会执行Transaction步骤中的4、5、6、7.但是如果我们使用peer invoke,那么会怎么样呢?比如如下的命令:

peer chaincode invoke -o orderer.example.com:7050  --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/cacerts/ca.example.com-cert.pem  -C mychannel -n mycc -c '{"Args":["query","a"]}'

那么从代码上来看,虽然我们是一个查询,却会以Transaction的生命周期来处理。

 

Fabric不支持对同一个数据的并发事务处理,也就是说,如果我们同时运行了a->b 10元,b->a 10元,那么只会第一条Transaction成功,而第二条失败。因为在Committer节点进行读写集版本验证的时候,第二条Transaction会验证失败。这是我完全无法接受的一点!

Fabric是异步的系统,在Endorser的时候a->b 10元,b->a 10元都会返回给SDK成功,而第二条Transaction在Committer验证失败后不进行State Database的写入,但是并不会通知Client SDK,所以必须使用EventHub通知Client或者Client重新查询才能知道是否写入成功。

不管在提交节点对事务的读写数据版本验证是否通过,因为Block已经在Orderer节点生成了,所以Block是被整块写入区块链的,而在State Database不会写入,所以会在Transaction之外的地方标识该Transaction是无效的。

 

 

 

 

 

转载于:https://my.oschina.net/u/568367/blog/1589730

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107825.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • bootstrap模态框关闭后清除模态框的数据

    bootstrap模态框关闭后清除模态框的数据

    2021年11月8日
  • 锁屏时钟APP_linux时钟同步服务器设置

    锁屏时钟APP_linux时钟同步服务器设置桌面锁屏时钟里的桌面美化功能非常多,并且也都很实用,不仅可以帮助用户把手机桌面设置的更加简洁,查找东西变得更方便,而且用户还能够使用自定义设置的方式来将自己手机桌面的内容,进行不同的展示,桌面锁屏时钟app就算在锁屏的状态下也能够显示当前的时间,非常便捷。桌面锁屏时钟优势1.一款极简实用时钟,适合每一个喜欢简约的你。2.主界面是自带时间、日期、天气温度的LED电子数字时钟。3.经典的动态翻页效果,…

    2022年9月29日
  • linux网络配置出现E325,linux下vi操作出现E325: ATTENTION的解决方法

    linux网络配置出现E325,linux下vi操作出现E325: ATTENTION的解决方法MyBatis,动态传入表名,字段名的解决办法转载:http://luoyu-ds.iteye.com/blog/1517607今天做项目,遇到的问题就是需求修改数据表的记录,而且字段名都不是固定的,也就是说是需要通过参数传入的,本来这也不是…JavaScript–事件模型(转)在各种浏览器中存在三种事件模型:原始事件模型(originaleventmodel),DOM2事件模型…

    2022年5月19日
  • activity生命周期_activity的生命周期有哪些状态

    activity生命周期_activity的生命周期有哪些状态在我的Android四大组件详解一文中已经对Activity的生命周期进行过一个比较详细的介绍。这篇文章我们再来更加深入地介绍Activity的生命周期。一、Activity的生命周期方法如下:onCreate、onStart、onRestart、onResume、onPause、onStop、onDestroy依次介绍:onCreate:表示Activity正在被创建,这是生命周

    2022年8月16日
  • bootstraptable之uniqueId

    bootstraptable之uniqueId如何设置每行唯一的标识符uniqueId$(‘#dataTable’).bootstrapTable(‘destroy’).bootstrapTable({columns:[{field:’OrganizeID’,title:’部门编号’,…

    2022年10月25日
  • javaWeb前后台交互(Jdbc+Jsp+Servlet+mysql)

    javaWeb前后台交互(Jdbc+Jsp+Servlet+mysql)最近翻看以前的笔记,想到以前自己学习完servlet+jsp+mysql却怎么也把前后台连接不上,最后自己折腾好几天,终于搭建成功,写这个笔记也算是记录一下,代码是那个时候的代码,现在稍微加工了下。可以照下面步骤先把前后台连接上,有点成就感,给自己打打气加油加油,然后再研究代码怎么写的。毕竟都是从那个时候爬(自学坑太多)过来的。(这段话只是针对你我等小白,大神请自动忽略,谢谢配合)这次就写最简…

    2022年6月13日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号