回顧一下我之前的一篇部落格,在Fabric 1.0中,我們存在3種型別的資料儲存,一種是基於檔案系統的區塊鏈資料,這個跟比特幣很像,比特幣也是檔案形式儲存的。Fabric1.0中的區塊鏈儲存了Transaction訂單讀寫集。而讀寫集到底是讀什麼?寫什麼?其實就是我們的State Database,也叫做World State,裡面以鍵值對的方式儲存了我們在ChainCode中操作的業務資料。另外還有就是對歷史資料和區塊鏈索引的資料庫。
區塊鏈是檔案系統,這個目前不支援更改,歷史資料和區塊鏈的索引是LevelDB,這個也不能更改。而對於State Database,由於和業務相關,所以提供了替換資料庫,目前支援預設的LevelDB和使用者可選擇的CouchDB。這裡要說到2點,一個是在0.6的時候其實用的RockDB,但是由於License的考慮,所以在1.0改成了LevelDB。另外就是CouchDB也不一定是最優的,很多人還考慮到MongoDB或者MySQL等,但是由於現在Fabric那邊開發資源有限,所以在1.0還不會做,以後可能會實現。
CouchDB安裝
下面我們來說一說這個CouchDB。
CouchDB是一個完全域性域RESTful API的鍵值資料庫,也就是說我們不需要任何客戶端,只需要通過HTTP請求就可以運算元據庫了。LevelDB是Peer的本地資料庫,那麼肯定是和Peer一對一的關係,那麼CouchDB是個網路資料庫,應該和Peer是什麼樣一個關係呢?在生產環境中,我們會為每個組織部署節點,而且為了高可用,可能會在一個組織中部署多個Peer。同樣我們在一個組織中也部署多個CouchDB,每個Peer對應一個CouchDB。
HyperLedger在Docker Hub上也釋出了CouchDB的映象,為了能夠深入研究CouchDB和Fabric的整合,我們就採用官方釋出的CouchDB來做。
docker pull klaemo/couchdb
【注意,如果我們是docker pull couchdb,那麼只能獲得1.6版本的CouchDB,而要獲得最新的2.0版,就需要用上面這個映象。】
可以獲得官方的CouchDB映象。CouchDB在啟動的時候需要指定一個本地資料夾對映成CouchDB的資料儲存資料夾,所以我們可以在當前使用者的目錄下建立一個資料夾用於存放資料。
mkdir couchdb
下載完成後,我們只需要執行以下命令即可啟用一個CouchDB的例項:
docker run -p 5984:5984 -d --name my-couchdb -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v ~/couchdb:/opt/couchdb/data klaemo/couchdb
啟動後我們開啟瀏覽器,訪問Linux的IP的5984埠的URL,比如我的Linux是192.168.100.129,那麼URL是:
http://192.168.100.129:5984/_utils
這個時候我們就可以看到CouchDB的Web管理介面了。輸入使用者名稱admin密碼password即可進入。
現在是一個空資料庫,我們將CouchDB和Peer結合起來後再看會是什麼樣的效果。
配置CouchDB+Fabric環境
先刪除剛才建立的CouchDB容器:
docker rm -f my-couchdb
首先我們是4個Peer+1Orderer的模式,所以我們先建立4個CouchDB資料庫:
cd ~ mkdir couchdb0 mkdir couchdb1 mkdir couchdb2 mkdir couchdb3 docker run -p 5984:5984 -d --name couchdb0 -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v ~/couchdb0:/opt/couchdb/data klaemo/couchdb docker run -p 6984:5984 -d --name couchdb1 -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v ~/couchdb1:/opt/couchdb/data klaemo/couchdb docker run -p 7984:5984 -d --name couchdb2 -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v ~/couchdb2:/opt/couchdb/data klaemo/couchdb docker run -p 8984:5984 -d --name couchdb3 -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v ~/couchdb3:/opt/couchdb/data klaemo/couchdb
然後我們需要啟動Fabric了。Fabric的準備環境,可以參見我們這篇部落格:http://www.cnblogs.com/studyzy/p/6973334.html
官方已經提供了多個Docker-compose檔案,如果我們使用的是./network_setup.sh up命令,那麼啟用的就是docker-compose-cli.yaml這個檔案。如果要基於這個yaml檔案啟用CouchDB的Peer,則開啟該檔案,並編輯其中的Peer節點,改為如下的形式:
peer0.org1.example.com:
container_name: peer0.org1.example.com
environment:
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=192.168.100.129:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=password
extends:
file: base/docker-compose-base.yaml
service: peer0.org1.example.com
這裡的192.168.100.129:5984是我對映CouchDB後的Linux的IP地址和IP。然後是設定使用者名稱和密碼。把4個Peer的配置都改好後,儲存,我們試著啟用Fabric:
./network_setup.sh up
等Fabric啟動完成並執行了ChainCode測試後,我們重新整理http://192.168.100.129:5984/_utils ,可以看到以Channel名字建立的Database,另外還有幾個是系統資料庫。
點進mychannel資料庫,我們可以看到其中的資料內容。點選“Mango Query”可以編寫查詢,預設提供的查詢可以點選Run Query按鈕查詢所有的資料結果:
CouchDB的直接查詢
接下來我們使用Linux的curl來查詢CouchDB資料庫。
比如我們要看看mychannel資料庫下有哪些資料:
curl http://192.168.100.129:5984/mychannel/_all_docs
可以看到我執行了一些ChainCode後的State DATABASE結果:
{"total_rows":7,"offset":0,"rows":[
{"id":"devincc\u0000a","key":"devincc\u0000a","value":{"rev":"2-a979bf6c2716ecae6d106999f833a59c"}},
{"id":"devincc\u0000b","key":"devincc\u0000b","value":{"rev":"2-ad1c549305fd277097180405f96bdcd8"}},
{"id":"lscc\u0000devincc","key":"lscc\u0000devincc","value":{"rev":"1-05d2cd0b344c4dd8a8d1a3ffd7332544"}},
{"id":"lscc\u0000mycc","key":"lscc\u0000mycc","value":{"rev":"1-2cba0344b1610b9d9254bbafbda5e9b1"}},
{"id":"mycc\u0000a","key":"mycc\u0000a","value":{"rev":"2-588a45b289359afa9dc6e5e7866eaf97"}},
{"id":"mycc\u0000b","key":"mycc\u0000b","value":{"rev":"2-54e6639a858b0f91298c9a354484513a"}},
{"id":"statedb_savepoint","key":"statedb_savepoint","value":{"rev":"10-6ccde2a55c71d7d6a70d9333d119fc8e"}}
]}
如果我們要查詢其中的一條資料,只需要用/ChannelId/id來查詢,比如查詢:statedb_savepoint
curl http://192.168.100.129:5984/mychannel/statedb_savepoint
返回的結果:
{"_id":"statedb_savepoint","_rev":"10-6ccde2a55c71d7d6a70d9333d119fc8e","BlockNum":4,"TxNum":0,"UpdateSeq":"19-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0jLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclsuJVdwCi7j4h8x5A1AHdx5kFAI6sYwk"}
麻煩的是業務資料是“ChainCodeName\u0000資料”這樣的格式的ID,而如果我們要通過這個ID查詢,那麼就根本找不到啊!
curl http://192.168.100.129:5984/mychannel/mycc\u0000a
{"error":"not_found","reason":"missing"}
正確的做法是把\u0000替換為%00,也就是說我們的查詢應該是:
curl http://192.168.100.129:5984/mychannel/mycc%00a
正確返回結果:
{"_id":"mycc\u0000a","_rev":"2-588a45b289359afa9dc6e5e7866eaf97","chaincodeid":"mycc","version":"4:0","_attachments":{"valueBytes":{"content_type":"application/octet-stream","revpos":2,"digest":"md5-hhOYXsSeuPdXrmQ56Hm7Kg==","length":2,"stub":true}}}
Fabric可能會遇到的問題
雖然區塊鏈是一個只能插入和查詢的資料庫,但是我們的業務資料是存放在State Database中的,如果我們直接修改了CouchDB的資料,那麼接下來的查詢和事務是直接基於修改後的CouchDB的,並不會去檢查區塊鏈中的記錄,所以理論上是可以通過直接改CouchDB來實現業務資料的修改。
我們以官方的Marble為例,看看修改CouchDB會怎麼樣?
具體操作步驟如下:
1.Install,instantiate和初始化資料:
peer chaincode install -n marbles02 -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/marbles02 peer chaincode instantiate -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 marbles02 -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/marbles02 -c '{"Args":["init"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" 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 marbles02 -c '{"Args":["initMarble","marble2","red","50","tom"]}' peer chaincode query -C mychannel -n marbles02 -c '{"Args":["readMarble","marble2"]}'
我們可以看到通過curl直接查詢CouchDB中的資料:
curl http://192.168.100.129:5984/mychannel/marbles02%00marble2
{"_id":"marbles02\u0000marble2","_rev":"1-a1844f47b9ed94294b430c9a9a6f543b","chaincodeid":"marbles02","data":{"docType":"marble","name":"marble2","color":"red","size":50,"owner":"tom"},"version":"6:0"}
如果我們要修改其中的資料,把顏色改成green,大小改成10,那麼我們可以執行:
curl -X PUT http://192.168.100.129:5984/mychannel/marbles02%00marble2 -d '{"_id":"marbles02\u0000marble2","_rev":"1-a1844f47b9ed94294b430c9a9a6f543b","chaincodeid":"marbles02","data":{"docType":"marble","name":"marble2","color":"green","size":10,"owner":"tom"},"version":"6:0"}'
系統返回結果:
{"ok":true,"id":"marbles02\u0000marble2","rev":"2-6ffc6652cfc707f8352a5f06c3ce1ce6"}
我們在4個CouchDB中都執行這個命令,把4個資料庫的資料都改了。
接下來我們通過ChainCode來查詢,看看會怎麼樣。
peer chaincode query -C mychannel -n marbles02 -c '{"Args":["readMarble","marble2"]}'
返回結果:
Query Result: {"color":"green","docType":"marble","name":"marble2","owner":"tom","size":10}
可以看到資料已經變成新的值,那麼接下來執行其他的Transaction會怎麼樣?我們試一試轉賬操作:
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 marbles02 -c '{"Args":["transferMarble","marble2","jerry"]}'
系統返回成功,我們再查一下呢
peer chaincode query -C mychannel -n marbles02 -c '{"Args":["readMarble","marble2"]}'
Query Result: {"color":"green","docType":"marble","name":"marble2","owner":"jerry","size":10}
所以我們對CouchDB資料庫的更改都是有效的,在Fabric看來似乎並不知道我們改了CouchDB的內容。