【Fabricv2.3.2】教程-使⽤CouchDB
使⽤ CouchDB
本教程将讲述在 Hyperledger Fabric 中使⽤ CouchDB 作为状态数据库的步骤。现在, 你应该已经熟悉 Fabric 的概念并且已经浏览了⼀些⽰例和教程。
注解
这个教程使⽤了 Fabric v2.0 引进的新功能链码⽣命周期。 如果你想要使⽤以前版本的⽣命周期模型来操作链码的索引功能, 访问v1.4 版本的 使⽤ CouchDB .
本教程将带你按如下步骤与学习:
在 Hyperledger Fabric 中启⽤ CouchDB
创建⼀个索引
将索引添加到你的链码⽂件夹
安装和定义链码
查询CouchDB 状态数据库
查询和索引的最佳实践
在 CouchDB 状态数据库查询中使⽤分页
升级索引
删除索引
想要更深⼊的研究 CouchDB 的话,请参阅 使⽤ CouchDB 作为状态数据库 ,关于 Fabric 账 本的跟多信息请参阅 Ledger 主题。下边的教程将详细讲述如何在你的区 块链⽹络中使⽤ CouchDB 。
本教程将使⽤ Marbles sample 作为演⽰在 Fabric 中使⽤ CouchDB 的⽤例,并且将会把 Marbles 部署在 构建你的第⼀个⽹络(BYFN)教程⽹络上。
为什么是 CouchDB ?
Fabric ⽀持两种类型的节点数据库。LevelDB 是默认嵌⼊在 peer 节点的状态数据库。 LevelDB ⽤于将链码数据存储为简单的键-值对,仅⽀持键、键范围和复合键查询。CouchDB 是⼀ 个可选的状态数
据库,⽀持以 JSON 格式在账本上建模数据并⽀持富查询,以便您查询实际数据 内容⽽不是键。CouchDB 同样⽀持在链码中部署索引,以便⾼效查询和对⼤型数据集的⽀持。
leveldb使用为了发挥 CouchDB 的优势,也就是说基于内容的 JSON 查询,你的数据必须以 JSON 格式 建模。你必须在设置你的⽹络之前确定使⽤LevelDB 还是 CouchDB 。由于数据兼容性的问 题,不⽀持节点从 LevelDB 切换为 CouchDB 。⽹络中的所有节点必须使⽤相同的数据库类 型。如果你想 JSON 和⼆进制数据混合使⽤,你同样可以使⽤ CouchDB ,但是⼆进制数据只 能根据键、键范围和复合键查询。
在 Hyperledger Fabric 中启⽤ CouchDB
CouchDB 是独⽴于节点运⾏的⼀个数据库进程。在安装、管理和操作的时候有⼀些额外 的注意事项。有⼀个可⽤的 Docker 镜像CouchDB 并且我们建议它和节点运⾏在同⼀个服务器上。我们需要在每⼀个节点上安装⼀个 CouchDB 容器,并且更新每⼀个节点的配置⽂件 core.yaml ,将节点指向 CouchDB 容器。 core.yaml ⽂件的路径必须在环境变量 FABRIC_CFG_PATH 中指定:
对于 Docker 的部署,在节点容器中 FABRIC_CFG_PATH 指定的⽂件夹中的 core.yaml 是预先配置好的。如果你要使⽤ docker 环境,你可以通过重写 docker-compose-couch.yaml 中的环境变量来覆盖core.yaml
对于原⽣的⼆进制部署, core.yaml 包含在发布的构件中。
编辑 core.yaml 中的 stateDatabase 部分。将 stateDatabase 指定为 CouchDB 并且填写 couchDBConfig
相关的配置。在 Fabric 中配置 CouchDB 的更多细节,请参阅 CouchDB 配置 。
创建⼀个索引
为什么索引很重要?
索引可以让数据库不⽤在每次查询的时候都检查每⼀⾏,可以让数据库运⾏的更快和更⾼效。 ⼀般来说,对频繁查询的数据进⾏索引可以使数据查询更⾼效。为了充分发挥 CouchDB 的优 势 – 对 JSON 数据进⾏富查询的能⼒ – 并不需要索引,但是为了性能考虑强烈建议建⽴ 索引。另外,如果在⼀个查询中需要排序,CouchDB 需要在排序的字段有⼀个索引。
注解
没有索引的情况下富查询也是可以使⽤的,但是会在 CouchDB 的⽇志中抛出⼀个没有到索引的警告。如果⼀个富查询中包含了⼀个排序的说明,需要排序的那个字段 就必须有索引;否则,查询将会失败并抛出错误。
为了演⽰构建⼀个索引,我们将会使⽤来⾃ Marbles sample. 的数据。 在这个例⼦中, Marbles 的数据结构定义如下:
type marble struct {
ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
Name string `json:"name"` //the field tags are needed to keep case from bouncing around
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
}
在这个结构体中,( docType, name, color, size, owner )属性 定义了和资产相关的账本数据。 docType 属性⽤来在链码中区分可能需要单独查询的 不同数据类型的模式。当时使⽤ CouchDB 的时
候,建议包含 docType 属性来区分在链 码命名空间中的每⼀个⽂档。(每⼀个链码都需要有他们⾃⼰的 CouchDB 数据库,也就是 说,每⼀个链码都有它⾃⼰的键的命名空间。)
在 Marbles 数据结构的定义中, docType ⽤来识别这个⽂档或者资产是⼀个弹珠资产。 同时在链码数据库中也可能存在其他⽂档或者资产。数据库中的⽂档对于这些属性值来说都是 可查询的。
当为链码查询定义⼀个索引的时候,每⼀个索引都必须定义在⼀个扩展名为 *.json 的⽂本⽂件中,并且索引定义的格式必须为 CouchDB 索引的 JSON 格式。
需要以下三条信息来定义⼀个索引:
fields: 这些是常⽤的查询字段
name: 索引名
type: 它的内容⼀般是 json
例如,这是⼀个对字段 foo 的⼀个名为 foo-index 的简单索引。
{
"index":{
"fields":["foo"]
},
"name":"foo-index",
"type":"json"
}
可选地,设计⽂档( design document )属性 ddoc 可以写在索引的定义中。design document 是 CouchDB 结构,⽤于包含索引。索引可以以组的形式定义在设计⽂档中以提升效率,但是 CouchDB 建议每⼀个设计⽂档包含⼀个索引。
⼩技巧
当定义⼀个索引的时候,最好将 ddoc 属性和值包含在索引内。包含这个 属性以确保在你需要的时候升级索引,这是很重要的。它还使你能够明确指定要在查询上使⽤的索引。
这⾥有另外⼀个使⽤ Marbles ⽰例定义索引的例⼦,在索引 indexOwner 使⽤了多个字段 docType 和 owner 并且包含了 ddoc 属性:
{
"index":{
"fields":["docType","owner"] // Names of the fields to be queried
},
"ddoc":"indexOwnerDoc", // (optional) Name of the design document in which the index will be created.
"name":"indexOwner",
"type":"json"
}
在上边的例⼦中,如果设计⽂档 indexOwnerDoc 不存在,当索引部署的时候会⾃动创建 ⼀个。⼀个
索引可以根据字段列表中指定的⼀个或者多个属性构建,⽽且可以定义任何属性的 组合。⼀个属性可以存在于同⼀个 docType 的多个索引中。在下边的例⼦中, index1 只包含owner 属性, index2 包含 owner 和 color 属性, index3 包含 owner、 color 和 size 属性。另外,注意,根据 CouchDB 的建议,每⼀个索引的定义 都包含⼀个它们⾃⼰的 ddoc 值。
{
"index":{
"fields":["owner"] // Names of the fields to be queried
},
"ddoc":"index1Doc", // (optional) Name of the design document in which the index will be created.
"name":"index1",
"type":"json"
}
{
"index":{
"fields":["owner", "color"] // Names of the fields to be queried
},
"ddoc":"index2Doc", // (optional) Name of the design document in which the index will be created.
"name":"index2",
"type":"json"
}
{
"index":{
"fields":["owner", "color", "size"] // Names of the fields to be queried
},
"ddoc":"index3Doc", // (optional) Name of the design document in which the index will be created.
"name":"index3",
"type":"json"
}
⼀般来说,你为索引字段建模应该匹配将⽤于查询过滤和排序的字段。对于以 JSON 格式 构建索引的更多信息请参阅 CouchDB documentation 。
关于索引最后要说的是,Fabric 在数据库中为⽂档建⽴索引的时候使⽤⼀种成为 索引升温(index warming) 的模式。 CouchDB 直到下⼀次查询的时候才会索引新的或者更新的 ⽂档。Fabric 通过在每⼀个数据区块提交完之后请求索引更新的⽅式,来确保索引处于 ‘热(warm)’ 状态。这就确保了查询速度快,因为在运⾏查询之前不⽤索引⽂档。这个过程保 持了索引的现状,并在每次新数据添加到状态数据的时候刷新。
将索引添加到你的链码⽂件夹
当你完成索引之后,你需要把它打包到你的链码中,以便于将它部署到合适的元数据⽂件夹。你可以使⽤ peer lifecycle chaincode 命令安装链码。JSON 索引⽂件必须放在链码⽬录的 META-INF/statedb/couchdb/indexes 路径下。
下边的 Marbles ⽰例 展⽰了如何将索引打包到链码中。
这个例⼦包含了⼀个名为 indexOwnerDoc 的索引:
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
启动⽹络
Try it yourself
我们将会启动⼀个 Fabric 测试⽹络并且使⽤它来部署 marbles 链码。 使⽤下⾯的命令导航到 Fabric samples 中的⽬录 test-network :
cd fabric-samples/test-network
对于这个教程,我们希望在⼀个已知的初始状态进⾏操作。 下⾯的命令会删除正在进⾏的或停⽌的 docker 容器并且移除之前⽣成的构件:
./network.sh down
如果你之前从没运⾏过这个教程,在我们部署链码到⽹络之前你需要使⽤ vendor 来安装链码的依赖⽂件。 运⾏以下的命令:
cd../chaincode/marbles02/go
GO111MODULE=on go mod vendor
cd../../../test-network
在 test-network ⽬录中,使⽤以下命令部署带有 CouchDB 的测试⽹络:
./network.sh up createChannel -s couchdb
运⾏这个命令会创建两个使⽤ CouchDB 作为状态数据库的 fabric 节点。 同时也会创建⼀个排序节点和⼀个名为 mychannel 的通道
安装和定义链码
客户端应⽤程序通过链码和区块链账本交互。所以我们需要在每⼀个执⾏和背书交易的节点上安装链码。但是在我们和链码交互之前,通道中的成员需要⼀致同意链码的定义,以此 来建⽴链码的治理。在之前的章节中,我们演⽰了如何将索引添加到链码⽂件夹中以便索引和链码部署在⼀起。
链码在安装到 Peer 节点之前需要打包。我们可以使⽤ peer lifecycle chaincode package 命令来打包弹珠链码。
Try it yourself
1. 启动测试⽹络后,在你终端拷贝粘贴下⾯的环境变量,这样就可以使⽤ Org1 管理员⽤户和⽹络交互。 确保你在 test-network ⽬录
中。
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/ample/1.example/ export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/ample/users/ample/msp
export CORE_PEER_ADDRESS=localhost:7051
2. 使⽤下⾯的命令来打包 marbles 链码:
peer lifecycle chaincode package --path ../chaincode/marbles02/go --lang golang --label marbles_1
这个命令会创建⼀个名为 的链码包。
3. 使⽤下⾯的命令来安装链码包到节点上 ample:
peer lifecycle chaincode install
⼀个成功的安装命令会返回链码 id ,就像下⾯的返回信息:
2019-04-22 18:47:38.312 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nJmarbles_1: 0907c1f3d3574afca69946e1b6132691d58c2f5c5703df7fc3b692861e92ecd3\022\tmarbles_1">
2019-04-22 18:47:38.312 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: marbles_1:0907c1f3d3574af ca69946e1b6132691d58c2f5c5703df7fc3b692861e92ecd3
安装链码到 ample 后,我们需要让 Org1 同意链码定义。
4. 使⽤下⾯的命令来⽤你的当前节点查询已安装链码的 package ID 。
peer lifecycle chaincode queryinstalled
这个命令会返回和安装命令相同的 package ID 。 你应该看到类似下⾯的输出:
Installed chaincodes on peer:
Package ID: marbles_1:60ec9430b221140a45b96b4927d1c3af736c1451f8d432e2a869bdbf417f9787, Label: marbles_1
5. 将 package ID 声明为⼀个环境变量。 将 peer lifecycle chaincode queryinstalled 命令返回的 marbles_1 的 package ID 粘贴到下⾯的
命令中。 package ID 不是所有⽤户都⼀样,所以你需要使⽤终端返回的 package ID 来完成这个步骤。
export CC_PACKAGE_ID=marbles_1:60ec9430b221140a45b96b4927d1c3af736c1451f8d432e2a869bdbf417f9787
6. 使⽤下⾯的命令让 Org1 同意 marbles 链码定义。
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/example/orderers/orderer.e
xample/msp/ample-cert.pe m
peer lifecycle chaincode approveformyorg -o localhost:7050 --ample --channelID mychannel --name marble s --version 1.0 --signature-policy "OR('ber','ber')" --init-required --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA
命令成功运⾏的时候你应该看到和下⾯类似的信息:
2020-01-07 16:24:20.886 EST [chaincodeCmd] ClientWait -> INFO 001 txid [560cb830efa1272c85d2f41a473483a25f3b12715d55e22a69d55abc4658141 5] committed with status (VALID) at
在链码定义提交之前,我们需要⼤多数组织同意链码定义。这意味着我们需要 Org2 也同意该链码定义。因为我们不需要 Org2 背书链码并且不安装链码包到 Org2 的节点,所以 packageID 作为链码定义的⼀部分,我们不需要向 Org2 提供它。
7. 让终端使⽤ Org2 管理员⾝份操作。将下⾯的命令⼀起拷贝粘贴到节点容器并且⼀次性全部运⾏。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论