使用CouchDB做爲狀態數據庫html
狀態數據庫選項node
狀態數據庫包括LevelDB和CouchDB。LevelDB是嵌入在peer進程中的默認鍵/值狀態數據庫,CouchDB是一個可選的外部狀態數據庫。與LevelDB鍵/值存儲同樣,CouchDB能夠存儲任何以chaincode建模的二進制數據(CouchDB附件函數在內部用於非json二進制數據)。可是,當chaincode值(例如,資產)被建模爲JSON數據時,做爲JSON文檔存儲,CouchDB支持對chaincode數據進行豐富的查詢。git
LevelDB和CouchDB都支持核心chaincode操做,例如獲取和設置一個鍵(資產),並根據鍵進行查詢。鍵能夠經過範圍查詢,能夠對組合鍵進行建模,以支持針對多個參數的等價查詢。例如,做爲全部者的組合鍵,資產id能夠用於查詢某個實體擁有的全部資產。這些基於key的查詢能夠用於針對帳本的只讀查詢,以及更新總帳的事務。github
若是將資產建模爲JSON並使用CouchDB,那麼就可使用chaincode中的CouchDB JSON查詢語言對chaincode數據值執行復雜的富查詢,這些類型的查詢對於理解帳本上的內容頗有幫助。對於這些類型的查詢,事務協議響應一般對客戶端應用程序有用,但一般不會做爲事務提交到排序服務。事實上,也沒法保證結果集在chaincode執行與富查詢提交時間之間的穩定性,所以使用富查詢的結果去執行最終的事務更新操做是不合適的,除非能夠保證結果集在chaincode執行時間與提交時間之間的穩定性,或者能夠處理在後續交易中的潛在變化。例如,若是對Alice所擁有的全部資產執行一個富查詢並將其傳輸給Bob,那麼一個新的資產可能會被另外一個事務分配給Alice,這是在chaincode執行時間和提交時間之間的另外一個事務,可能此過程當中會錯過這個「虛值」。docker
CouchDB做爲一個獨立的數據庫進程與peer一塊兒運行,所以在設置、管理和操做方面有額外的考慮。咱們能夠考慮從默認的嵌入式LevelDB開始,若是須要額外的複雜的富查詢,能夠轉移到CouchDB。將chaincode資產數據建模爲JSON是一種很好的作法,這樣咱們就能夠在未來執行須要的複雜的富查詢。數據庫
使用Chaincode中的CouchDBjson
大多數chaincode的api均可以使用LevelDB或CouchDB狀態數據庫,例如GetState、PutState、GetStateByRange以及GetStateByPartialCompositeKey等。只有當使用CouchDB做爲狀態數據庫和在chaincode中做爲JSON的模型資產時,可使用GetQueryResult API和傳遞一個CouchDB查詢字符串來對狀態數據庫中的JSON執行富查詢。查詢字符串遵循CouchDB JSON查詢語法。api
fabric項目中的marbles02示例演示了使用來自chaincode的CouchDB查詢。它包括一個getMarblesByOwner()函數,它經過將一個owner id傳遞給chaincode來演示參數化查詢,而後查詢與「marble」docType匹配的JSON文檔的狀態數據,以及owner id使用JSON查詢語法:app
{
"selector":{
"docType":"marble",
"owner":<OWNER_ID>
}
}
爲了使JSON查詢更高效,而且對於任何具備排序的JSON查詢,都須要使用CouchDB中的索引。索引能夠和chaincode一塊兒打包 /META-INF/statedb/couchdb/indexes目錄。每一個索引必須在其本身的文本文件中經過擴展*.json被定義,其中索引定義爲JSON格式並引用CouchDB索引JSON語法,例如,爲了支持上面的marble查詢,提供了一個關於docType和全部者字段的示例索引:
ide
{
"index":{
"fields":[
"docType",
"owner"
]
},
"ddoc":"indexOwnerDoc",
"name":"indexOwner",
"type":"json"
}
這裏能夠找到示例索引。
注意:在1.1 alpha中,必須爲索引定義中引用的每一個字段指定「數據」封裝器。在隨後的版本中,指定「數據」封裝器的需求可能會被取消。
在chaincode的META-INF/statedb/couchdb/indexes目錄中的任何索引都將與chaincode一塊兒被打包和安裝在peer上。當chaincode即安裝在peer上,且又在peer的channel上實例化時,該索引將自動部署到peer的channel狀態數據庫(若是已配置爲使用CouchDB)。若是是先安裝了chaincode,而後纔在channel上實例化該chaincode,那麼該索引將會在chaincode實例化的時候進行部署。若是chaincode已經在一個channel上實例化了,而後又在一個peer上安裝了這個chaincode,該索引仍是會在chaincode實例化的時候進行部署。
在部署時,索引將自動被chaincode查詢利用。CouchDB能夠根據查詢中使用的字段自動肯定要使用的索引。或者,在選擇器查詢中,可使用use_index關鍵字指定索引。
在安裝的chaincode的後續版本中可能存在相同的索引。要更改索引,使用相同的索引名稱,但要更改索引定義。在安裝/實例化,索引定義會從新部署到peer的狀態數據庫。
若是所在channle已經擁有大量的數據,而且隨後安裝了chaincode,那麼索引的建立在實例化上可能須要一些時間。相似地,若是channel已經擁有大量的數據而且實例化了後續版本的chaincode,那麼建立索引也可能須要一些時間。因此須要儘可能避免在這些時間段內調用查詢狀態數據庫的chaincode函數,由於當索引被初始化時,chaincode查詢可能會超時。在事務處理期間,由於塊被提交給帳本總帳,因此索引將自動刷新。
CouchDB配置
經過將狀態數據庫配置選項從goleveldb更改成CouchDB,能夠將CouchDB做爲狀態數據庫啓用。此外,couchDBAddress須要配置爲指向由peer使用的CouchDB。若是將CouchDB配置一個用戶名和密碼,那麼用戶名和密碼屬性應該包含管理員用戶名和密碼。在couchDBConfig中提供了更多的選項,並在適當的地方進行了記錄。在重啓peer以後,修改過的core.yaml將會當即生效。
固然還能夠經過docker環境變量來覆蓋core.yaml值,例如CORE_LEDGER_STATE_STATEDATABASE和CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS。
如下是來自core.yaml的狀態數據庫選項:
state: # stateDatabase - options are "goleveldb", "CouchDB" # goleveldb - default state database stored in goleveldb. # CouchDB - store state database in CouchDB stateDatabase: goleveldb couchDBConfig: # It is recommended to run CouchDB on the same server as the peer, and # not map the CouchDB container port to a server port in docker-compose. # Otherwise proper security must be provided on the connection between # CouchDB client (on the peer) and server. couchDBAddress: couchdb:5984 # This username must have read and write authority on CouchDB username: # The password is recommended to pass as an environment variable # during start up (e.g. LEDGER_COUCHDBCONFIG_PASSWORD). # If it is stored here, the file must be access control protected # to prevent unintended users from discovering the password. password: # Number of retries for CouchDB errors maxRetries: 3 # Number of retries for CouchDB errors during peer startup maxRetriesOnStartup: 10 # CouchDB request timeout (unit: duration, e.g. 20s) requestTimeout: 35s # Limit on the number of records to return per query queryLimit: 10000
CouchDB被託管在docker容器中,經過使用Docker Compose腳原本利用Hyperledger Fabric設置CouchDB的username和password的能力,經過已有的環境變量設置,即COUCHDB_USER和COUCHDB_PASSWORD環境變量。
對於在使用Fabric提供的docker映像以外的CouchDB安裝,本地安裝。必須編輯ini文件以設置admin用戶名和密碼。
Docker Compose腳本只在建立容器時設置用戶名和密碼。若是要在容器建立後更改用戶名或密碼,則必須對本地.ini文件進行編輯。
注意:在每一個peer啓動時都讀取CouchDB的peer選項。
補充
在Hyperledger Fabric 1.0 從零開始(九)——Fabric多節點集羣生產啓動中已經設置了peer和cli的啓動配置,其中沒有加入couchdb,這裏補充下一個新的peer編寫方法,但願對各位有幫助。
後續在寫到ca的時候也會將ca啓動部分加入進來。
# Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # version: '2' services: couchdb: container_name: couchdb image: hyperledger/fabric-couchdb # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service, # for example map it to utilize Fauxton User Interface in dev environments. ports: - "5984:5984" peer0.xxx.example.com: container_name: peer0.xxx.example.com image: hyperledger/fabric-peer environment: - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 - CORE_PEER_ID=peer0.xxx.example.com - CORE_PEER_NETWORKID=anti - CORE_PEER_ADDRESS=peer0.xxx.example.com:7051 - CORE_PEER_CHAINCODELISTENADDRESS=peer0.xxx.example.com:7052 - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.xxx.example.com:7051 - CORE_PEER_LOCALMSPID=xxxMSP - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock # the following setting starts chaincode containers on the same # bridge network as the peers # https://docs.docker.com/compose/networking/ - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=anti_default - CORE_LOGGING_LEVEL=ERROR # - CORE_LOGGING_LEVEL=DEBUG - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true - CORE_PEER_GOSSIP_USELEADERELECTION=true - CORE_PEER_GOSSIP_ORGLEADER=false - CORE_PEER_PROFILE_ENABLED=false - CORE_PEER_TLS_ENABLED=false - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt volumes: - /var/run/:/host/var/run/ - ./crypto-config/peerOrganizations/xxx.example.com/peers/peer0.xxx.example.com/msp:/etc/hyperledger/fabric/msp - ./crypto-config/peerOrganizations/xxx.example.com/peers/peer0.xxx.example.com/tls:/etc/hyperledger/fabric/tls working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer command: peer node start ports: - 7051:7051 - 7052:7052 - 7053:7053 depends_on: - couchdb networks: default: aliases: - anti extra_hosts: - "orderer0.example.com:x.x.x.41" - "orderer1.example.com:x.x.x.42" - "orderer2.example.com:x.x.x.43" cli: container_name: cli image: hyperledger/fabric-tools tty: true environment: - GOPATH=/opt/gopath - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_LOGGING_LEVEL=ERROR # - CORE_LOGGING_LEVEL=DEBUG - CORE_PEER_ID=cli - CORE_PEER_ADDRESS=peer0.xxx.example.com:7051 - CORE_PEER_LOCALMSPID=xxxMSP - CORE_PEER_TLS_ENABLED=false - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/xxx.example.com/peers/peer0.xxx.example.com/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/xxx.example.com/peers/peer0.xxx.example.com/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/xxx.example.com/peers/peer0.xxx.example.com/tls/ca.crt - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/xxx.example.com/users/Admin@xxx.example.com/msp working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer volumes: - /var/run/:/host/var/run/ - ./chaincode/go/:/opt/gopath/src/github.com/hyperledger/fabric/antimoth/anti/chaincode/go - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts depends_on: - peer0.xxx.example.com extra_hosts: - "orderer0.example.com:x.x.x.41" - "orderer1.example.com:x.x.x.42" - "orderer2.example.com:x.x.x.43" - "peer0.xxxx.example.com:x.x.xx.251"