来自:mjsws > 馆藏分类
配色: 字号:
Hyperledger Fabric启用CouchDB为状态数据库
2019-01-06 | 阅:  转:  |  分享 
  
HyperledgerFabric启用CouchDB为状态数据库一.概述1.数据请求流超级账本采用背书/共识模型,模拟执行和区块验证是在
不同角色的节点中分开执行的。模拟执行是并发的,这样可以提高扩展性和吞吐量:背书节点:模拟执行链码Peer节点:验证交易并提交2.超
级账本存储元素超级账本包含以下元素:账本编号:快速查询存在哪些账本账本数据:实际的区块数据存储区块索引:快速查询区块/交易状态
数据:最新的世界状态数据历史数据:跟踪键的历史每个Peer节点会维护四个DB,分别为:账本索引库(IdStore):存储Cha
inID状态数据库(StateDB):存储worldstate历史数据库(HistoryDB):存储Key的版本变化区块索引
库(BlockIndex):存储Block索引3.状态数据库状态数据库可选类型包括LevelDB和CouchDB。LevelDB是
嵌入在peer进程中的默认键/值状态数据库,CouchDB是一个可选的外部状态数据库。与LevelDB键/值存储一样,CouchD
B可以存储任何以chaincode建模的二进制数据(CouchDB附件函数在内部用于非json二进制数据)。但是,当chainco
de值(例如,资产)被建模为JSON数据时,作为JSON文档存储,CouchDB支持对chaincode数据进行丰富的查询。Lev
elDB和CouchDB都支持核心chaincode操作,例如获取和设置一个键(资产),并根据键进行查询。键可以通过范围查询,可以
对组合键进行建模,以支持针对多个参数的等价查询。例如,作为所有者的组合键,资产id可以用于查询某个实体拥有的所有资产。这些基于ke
y的查询可以用于针对账本的只读查询,以及更新总账的事务。如果将资产建模为JSON并使用CouchDB,那么就可以使用chainco
de中的CouchDBJSON查询语言对chaincode数据值执行复杂的富查询,这些类型的查询对于理解账本上的内容很有帮助。对
于这些类型的查询,事务协议响应通常对客户端应用程序有用,但通常不会作为事务提交到排序服务。事实上,也无法保证结果集在chainco
de执行与富查询提交时间之间的稳定性,因此使用富查询的结果去执行最终的事务更新操作是不合适的,除非可以保证结果集在chaincod
e执行时间与提交时间之间的稳定性,或者可以处理在后续交易中的潜在变化。例如,如果对Alice所拥有的所有资产执行一个富查询并将其传
输给Bob,那么一个新的资产可能会被另一个事务分配给Alice,这是在chaincode执行时间和提交时间之间的另一个事务,可能此
过程中会错过这个“虚值”。CouchDB作为一个独立的数据库进程与peer一起运行,因此在设置、管理和操作方面有额外的考虑。我们可
以考虑从默认的嵌入式LevelDB开始,如果需要额外的复杂的富查询,可以转移到CouchDB。将chaincode资产数据建模为J
SON是一种很好的做法,这样我们就可以在将来执行需要的复杂的富查询。二.启用CouchDB本文均采用HyperledgerFa
bric1.2中fabric-samples中相关组件与资源,在测试环境(fabric-samples/chaincode-doc
ker-devmode)通过Docker启动CouchDB服务1.配置CouchDB启动信息参考:fabric-samples/f
irst-network/docker-compose-couch.yamlcouchdb0:container_name:
couchdb0image:hyperledger/fabric-couchdb#PopulatetheCOUCHDB
_USERandCOUCHDB_PASSWORDtosetanadminuserandpasswordhttp
://www.letaoqpyx.com#forCouchDB.ThiswillpreventCouchDBfr
omoperatinginan"AdminParty"mode.environment:-COUCHDB_USE
R=-COUCHDB_PASSWORD=#Comment/Uncommenttheportmappingifyo
uwanttohide/exposetheCouchDBservice,#forexamplemapitt
outilizeFauxtonUserInterfaceindevenvironments.ports:-"5
984:5984"networks:-byfn修改:fabric-samples/chaincode-docker-devm
ode/docker-compose-simple.yaml末尾添加并修改couchdb:container_name:c
ouchdbimage:hyperledger/fabric-couchdb#PopulatetheCOUCHDB_U
SERandCOUCHDB_PASSWORDtosetanadminuserandpassword#for
CouchDB.ThiswillpreventCouchDBfromoperatinginan"AdminP
arty"mode.environment:-COUCHDB_USER=-COUCHDB_PASSWORD=#Co
mment/Uncommenttheportmappingifyouwanttohide/exposetheC
ouchDBservice,http://www.f-1.cc#forexamplemapittoutilize
FauxtonUserInterfaceindevenvironments.ports:-"5984:5984"
2.配置CouchDB连接信息参考fabric-samples/first-network/docker-compose-couc
h.yamlpeer0.org1.example.com:environment:-CORE_LEDGER_STATE_S
TATEDATABASE=CouchDB-CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADD
RESS=couchdb0:5984#TheCORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME
andCORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD#providethecrede
ntialsforledgertoconnecttoCouchDB.Theusernameandpasswo
rdmust#matchtheusernameandpasswordsetfortheassociated
CouchDB.-CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=-CORE_LEDGE
R_STATE_COUCHDBCONFIG_PASSWORD=depends_on:-couchdb0修改:fabric-s
amples/chaincode-docker-devmode/docker-compose-simple.yaml中peer模
块修改前peer:container_name:peerimage:hyperledger/fabric-peeren
vironment:-CORE_PEER_ID=peer-CORE_PEER_ADDRESS=peer:7051-CO
RE_PEER_GOSSIP_EXTERNALENDPOINT=peer:7051-CORE_PEER_LOCALMSPID=
DEFAULT-CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock-COR
E_LOGGING_LEVEL=DEBUG-CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/
mspvolumes:-/var/run/:/host/var/run/-./msp:/etc/hyperledger/
mspworking_dir:/opt/gopath/src/github.com/hyperledger/fabric/pe
ercommand:peernodestart--peer-chaincodedev=true-oorderer:7
050ports:-7051:7051-7053:7053depends_on:-orderer修改后peer:
container_name:peerimage:hyperledger/fabric-peerenvironment:
-CORE_PEER_ID=peer-CORE_PEER_ADDRESS=peer:7051-CORE_PEER_GO
SSIP_EXTERNALENDPOINT=peer:7051-CORE_PEER_LOCALMSPID=DEFAULT-
CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock-CORE_LOGGING_
LEVEL=DEBUG-CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp-CORE
_LEDGER_STATE_STATEDATABASE=CouchDB-CORE_LEDGER_STATE_COUCHDBCO
NFIG_COUCHDBADDRESS=couchdb:5984-CORE_LEDGER_STATE_COUCHDBCONFI
G_USERNAME=-CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=volumes:
-/var/run/:/host/var/run/-./msp:/etc/hyperledger/mspworking_d
ir:/opt/gopath/src/github.com/hyperledger/fabric/peercommand:p
eernodestart--peer-chaincodedev=true-oorderer:7050ports:-
7051:7051-7053:7053depends_on:-orderer-couchdb注意JSON文件的格式以
及配置信息的一致性,如couchdb名称等3.启动测试环境#docker-compose-fdocker-compose-
simple.yamlup-d#dockercontainerls三.编写链码1.代码结构代码包:testdb代码文件
domain.go//数据结构代码main.go//业务测试代码2.数据结构packagemaintypeBillStru
ctstruct{ObjectTypestring`json:"DocType"`//对象类型定义BillInfoIDs
tring`json:"BillInfoID"`//票据IDBillInfoAmtstring`json:"BillIn
foAmt"`//票据金额BillInfoTypestring`json:"BillInfoType"`//票据类型
BillIsseDatastring`json:"BillIsseData"`//出票日期BillDueDatestr
ing`json:"BillDueDate"`//到期日期HoldrAcctstring`json:"HoldrAcct
"`//持票人名称HoldrCmIDstring`json:"HoldrCmID"`//持票人IDWaitEndroseAc
ctstring`json:"WaitEndroseAcct"`//待背书人名称WaitEndorseCmIDstrin
g`json:"WaitEndorseCmID"`//待背书人ID}3.测试代码请仔细阅读注释信息,此处不做代码分割描述pac
kagemainimport("github.com/hyperledger/fabric/core/chaincode/s
him""fmt""github.com/hyperledger/fabric/protos/peer""encoding/
json""bytes")//定义结构体CouchDBChaincode,作为shim.ChaincodeStubInterfa
ce实现类对象typeCouchDBChaincodestruct{}//重写shim.ChaincodeStubInter
face接口的Init方法func(tCouchDBChaincode)Init(stubshim.ChaincodeS
tubInterface)peer.Response{returnshim.Success(nil)}//重写shim.C
haincodeStubInterface接口的Invoke方法func(tCouchDBChaincode)Invoke
(stubshim.ChaincodeStubInterface)peer.Response{//获取用户意图与参数fu
n,args:=stub.GetFunctionAndParameters()//根据用户意图判断使用何种实现函数if
fun=="billInit"{returnbillInit(stub)}elseiffun=="query
Bills"{returnqueryBills(stub,args)}elseiffun=="queryWai
tBills"{returnqueryWaitBills(stub,args)}//如果用户意图不符合如上,进行错误提
示returnshim.Error("非法操作,指定的函数名无效")}//billInit函数:初始化票据数据funcbil
lInit(stubshim.ChaincodeStubInterface)peer.Response{/定义第一个票据
:持票人名称:AAA持票人ID:AID待背书人名称:无待背书人ID:无/billA:=BillStruct{Objec
tType:"billObj",BillInfoID:"POC001",BillInfoAmt:"1000",BillInf
oType:"111",BillIsseData:"20180501",BillDueDate:"20180508",Hol
drAcct:"AAA",HoldrCmID:"AID",WaitEndroseAcct:"",WaitEndorseCm
ID:"",}//通过json.Marshal方法对票据进行序列化操作http://www.gw638.cnbillABy
te,_:=json.Marshal(billA)//通过stub.PutState方法存储序列化后的字节数组err:
=stub.PutState(billA.BillInfoID,billAByte)iferr!=nil{retu
rnshim.Error("初始化第一个票据失败:"+err.Error())}billB:=BillStruct{
ObjectType:"billObj",BillInfoID:"POC002",BillInfoAmt:"1000",B
illInfoType:"111",BillIsseData:"20180501",BillDueDate:"20180508
",HoldrAcct:"AAA",HoldrCmID:"AID",WaitEndroseAcct:"BBB",Wait
EndorseCmID:"BID",}billBByte,_:=json.Marshal(billB)err=s
tub.PutState(billB.BillInfoID,billBByte)iferr!=nil{return
shim.Error("初始化第二个票据失败:"+err.Error())}billC:=BillStruct{Ob
jectType:"billObj",BillInfoID:"POC003",BillInfoAmt:"1000",Bill
InfoType:"111",BillIsseData:"20180501",BillDueDate:"20180508",
HoldrAcct:"BBB",HoldrCmID:"BID",WaitEndroseAcct:"CCC",WaitEnd
orseCmID:"CID",}billCByte,_:=json.Marshal(billC)err=stub
.PutState(billC.BillInfoID,billCByte)iferr!=nil{returnshi
m.Error("初始化第三个票据失败:"+err.Error())}billD:=BillStruct{Objec
tType:"billObj",BillInfoID:"POC004",BillInfoAmt:"1000",BillInf
oType:"111",BillIsseData:"20180501",BillDueDate:"20180508",Hol
drAcct:"CCC",HoldrCmID:"CID",WaitEndroseAcct:"BBB",WaitEndors
eCmID:"BID",}billDByte,_:=json.Marshal(billD)err=stub.Pu
tState(billD.BillInfoID,billDByte)iferr!=nil{returnshim.E
rror("初始化第四个票据失败:"+err.Error())}returnshim.Success([]byte("所
有票据初始化成功"))}//queryBills函数:批量查询指定用户的持票列表funcqueryBills(stubshim
.ChaincodeStubInterface,args[]string)peer.Response{//判断是否有参数
传入iflen(args)!=1{returnshim.Error("必须指定持票人的证件号码")}//将第一个
参数作为用户IDholdrCmID:=args[0]/将CouchDB查询字符串拼接成一个JSON串,格式如下:{"
selector":{"docType":"billObj","HoldrCmID":"%s"}}/query
String:=fmt.Sprintf("{\"selector\":{\"DocType\":\"billObj\",\"H
oldrCmID\":\"%s\"}}",holdrCmID)//通过自定义的getBillByQueryString函数进行
数据查询操作result,err:=getBillByQueryString(stub,queryString)if
err!=nil{returnshim.Error("根据持票人的证件号码批量查询持票人持有票据列表时发生错误"+e
rr.Error())}returnshim.Success(result)}//queryWaitBills函数:批量查询
指定用户的待背书票据列表funcqueryWaitBills(stubshim.ChaincodeStubInterface,
args[]string)peer.Response{iflen(args)!=1{returnshim.E
rror("必须指定待背书人的证件号码")}waitEndorseCmID:=args[0]queryString:=
fmt.Sprintf("{\"selector\":{\"docType\":\"billObj\",\"WaitEndors
eCmID\":\"%s\"}}",waitEndorseCmID)result,err:=getBillByQuery
String(stub,queryString)iferr!=nil{returnshim.Error("根据待背
书人的证件号码批量查询待背书票据列表时发生错误"+err.Error())}returnshim.Success(res
ult)}//自定义函数:getBillByQueryString:根据指定的查询字符串(CouchDB查询语句)查询数据func
getBillByQueryString(stubshim.ChaincodeStubInterface,queryStri
ngstring)([]byte,error){//通过stub.GetQueryResult方法获取迭代器iterat
oriterator,err:=stub.GetQueryResult(queryString)iferr!=ni
l{returnnil,err}//延迟关闭迭代器iteratordeferiterator.Close()//
定义字节缓冲变量varbufferbytes.Buffer//定义分割符varisSplitbool//对迭代器进
行遍历操作foriterator.HasNext(){//通过迭代器的Next()方法获取下一个对象的Key与Value值
(queryresult.KV)result,err:=iterator.Next()iferr!=nil{
returnnil,err}ifisSplit{buffer.WriteString(";")}//定义格式/
/key:result.keyresult.Valuebuffer.WriteString("key:")buffer.W
riteString(result.Key)buffer.WriteString(",value:")buffer.Write
String(string(result.Value))//获取到第一个值后,将isSplit设置为true,用于跟第二个值进行
分割isSplit=true}//返回buffer对象的字节类型returnbuffer.Bytes(),nil}
funcmain(){//启动链码CouchDBChaincodeerr:=shim.Start(new(CouchD
BChaincode))//如有报错,提示报错信息iferr!=nil{fmt.Errorf(err.Error()
)}}四.安装链码1.上传链码上传链码包testdb至:fabric-samples/chaincode中#ls/home/
bruce/hyfa/fabric-samples/chaincode/testdb/domain.gomain.go2.编
译链码#cd/home/bruce/hyfa/fabric-samples/chaincode/testdb/#gobui
ld#lsdomain.gomain.gotestdb3.启动链码进入chaincode容器进行操作#docker
containerexec-itchaincodebash#进入chaincode容器进行操作#cdtestdb/
#CORE_PEER_ADDRESS=peer:7052CORE_CHAINCODE_ID_NAME=testCouchDB:
1.0./testdb2018-08-0510:33:37.063UTC[shim]SetupChaincodeLog
ging->INFO001Chaincodeloglevelnotprovided;defaultingto:
INFO2018-08-0510:33:37.063UTC[shim]SetupChaincodeLogging->
INFO002Chaincode(buildlevel:)startingup...4.安装与实例化链码进入cli
容器进行操作#dockercontainerexec-itclibash#peerchaincodeinstal
l-ntestCouchDB-v1.0-pchaincodedev/chaincode/testdb#peerch
aincodeinstantiate-ntestCouchDB-v1.0-Cmyc-c''{"Args":["in
it"]}''如有更新请用如下命令进行操作#peerchaincodeinstall-ntestCouchDB-v1.
1-pchaincodedev/chaincode/testdb#peerchaincodeupgrade-ntes
tCouchDB-v1.1-Cmyc-c''{"Args":["init"]}''五.测试链码1.初始化票据#peer
chaincodeinvoke-ntestCouchDB-Cmyc-c''{"Args":["billInit"]
}''2.查询指定用户所持票据#peerchaincodequery-ntestCouchDB-Cmyc-c''
{"Args":["queryBills","AID"]}''key:POC001,value:{"BillDueDate"
:"20180508","BillInfoAmt":"1000","BillInfoID":"POC001","Bil
lInfoType":"111","BillIsseData":"20180501","HoldrAcct":"AAA"
,"HoldrCmID":"AID","WaitEndorseCmID":"","WaitEndroseAcct":"
","docType":"billObj"};key:POC002,value:{"BillDueDate":"20
180508","BillInfoAmt":"1000","BillInfoID":"POC002","BillInfo
Type":"111","BillIsseData":"20180501","HoldrAcct":"AAA","Ho
ldrCmID":"AID","WaitEndorseCmID":"BID","WaitEndroseAcct":"BBB","docType":"billObj"}查询结果可以看到我们定义的分隔符;3.查询指定用户待背书票据#peerchaincodequery-ntestCouchDB-Cmyc-c''{"Args":["queryWaitBills","BID"]}''key:POC002,value:{"BillDueDate":"20180508","BillInfoAmt":"1000","BillInfoID":"POC002","BillInfoType":"111","BillIsseData":"20180501","HoldrAcct":"AAA","HoldrCmID":"AID","WaitEndorseCmID":"BID","WaitEndroseAcct":"BBB","docType":"billObj"};key:POC004,value:{"BillDueDate":"20180508","BillInfoAmt":"1000","BillInfoID":"POC004","BillInfoType":"111","BillIsseData":"20180501","HoldrAcct":"CCC","HoldrCmID":"CID","WaitEndorseCmID":"BID","WaitEndroseAcct":"BBB","docType":"billObj"}另外关于LevelDB,CouchDB还是MongoDB,今后可能随着HyperledgerFabric的版本变化而采取不同的数据库类型,我们拭目以待,现在唯一能做的,就是在已有的资源下面用HyperledgerFabric为业务场景创造最大的业务价值。
献花(0)
+1
(本文系mjsws首藏)