本教程说明了基于通道的事件的使用。这些事件与现有事件相似,可是特定于单个通道。在设置侦听器时,客户端处理基于通道的事件有一些新选项。从v1.1开始,基于通道的事件是Hyperledger Fabric Node.js客户端的新功能。javascript
有关Fabric入门的更多信息,请查看构建你的第一个网络或者手把手教你走进Hyperledger Fabric。html
如下假设了解Fabric网络(orderers和peer)以及Node应用程序开发,包括使用Javascript Promise。java
客户端应用程序可使用Fabric Node.js客户端注册“侦听器”以在将块添加到通道分类账时接收块。咱们将这些称为“基于通道的事件”,它们容许客户端开始接收来自特定块编号的块,从而容许事件处理在可能已丢失的块上正常运行。Fabric Node.js客户端还能够经过处理传入的块并查找特定的交易或链代码事件来协助客户端应用程序。这容许客户端应用程序被通知交易完成或任意链代码事件,而没必要在接收时执行多个查询或搜索块。node
该服务容许任何用户接收“过滤的”块事件(换句话说,不包含敏感信息)。接收“未过滤”的块事件须要对通道进行读访问。默认行为是链接以接收过滤的块事件。要链接以接收未过滤的块事件,请调用connect(true)
(参见下文)。git
请注意,若是你注册块事件而后提交交易,则不该对包含交易的块进行任何假设。特别是,你不该该假设你的交易处于与注册到对等方基于通道的事件服务以后收到的第一个块事件关联的块中。相反,你能够只注册一个交易事件。github
newChannelEventHub(peer)
:获取ChannelEventHub的新实例的Channel实例方法。getChannelEventHubsForOrg
:获取基于组织的ChannelEventHubs列表。若是省略组织名称,则使用当前用户的当前组织。registerBlockEvent(eventCallBack,errorCallBack,options)
:注册块事件。unregisterBlockEvent(reg_num)
:删除块注册。registerTxEvent(tx_id,eventCallBack,errorCallBack,options)
:注册特定的交易事件。unregisterTxEvent(tx_id)
:删除特定的交易注册。registerChaincodeEvent(ccid,eventCallBack,errorCallBack,options)
:注册链代码事件。unregisterChaincodeEvent(cc_handle)
:删除链代码事件注册。connect(full_block)
:使客户端通道事件中心与基于结构通道的事件服务链接。必须在您的ChannelEventHub实例接收事件以前进行此调用。当基于通道的事件中心与服务链接时,它将请求接收块或过滤的块。若是省略full_block参数,则默认为false,而且将请求过滤的块。调用connect()后,没法更改接收块或已过滤的块。disconnect()
:使客户端通道事件路由关闭与基于结构网络通道的事件服务的链接,并使用已注册的errorCallBacks通知全部当前通道事件注册的关闭。获取ChannelEventHub的新实例时必须包含此参数。使用链接配置文件时,该值能够是Peer实例或节点的名称,请参阅如何使用公共网络配置文件。正则表达式
eventCallback参数编程
必须包含此参数。这是当此通道接收新块时,在侦听特定交易或链代码事件时要通知的回调函数。promise
errorCallback参数网络
这是一个可选参数。这是在此通道事件路由关闭时要通知的回调函数。关闭多是由结构网络错误,网络链接问题或经过调用disconnect()
方法引发的。
options参数
这是一个可选参数。此参数将包含如下可选属性:
{integer} startBlock
(可选):此选项设置为事件检查的起始块号。包含时,将要求对等方基于通道的事件服务开始今后块编号发送块。这也是如何恢复监听或重放添加到分类账的遗漏块。默认值是分类账上最后一个块的编号。replay事件可能会混淆其余事件监听器;所以,当使用startBlock
和endBlock
时,ChannelEventHub
上只容许一个侦听器。当排除此参数时(由于它将正常),将要求事件服务开始从分类账上的最后一个块发送块。{integer} endBlock
(可选):此选项设置为事件检查的结束块编号。当包含时,将要求对等方的基于通道的事件服务在交付此块后中止发送块。这是如何重播添加到分类账的遗漏块。若是未包含startBlock
,则endBlock
必须等于或大于当前通道块高度。replay事件可能会混淆其余事件监听器;所以,当使用startBlock
和endBlock
时,ChannelEventHub
上只容许一个侦听器。{boolean} unregister
(可选):此选项设置指示在看到事件时应删除(取消注册)注册。当应用程序使用超时仅等待指定的时间来查看交易时,超时处理应包括交易事件侦听器的手动“取消注册”,以免意外调用事件回调。对于不一样类型的事件侦听器,此设置的默认值是不一样的。对于块侦听器,将end_block设置为选项时,默认值为true。对于交易侦听器,默认值为true。对于链码侦听器,默认值为false,由于匹配过滤器可能适用于许多交易。{boolean} disconnect
(可选):此选项设置指示ChannelEventHub
实例在看到事件后自动断开自身与对等方基于通道的事件服务的链接。除非设置了endBlock
,不然默认值为false,那么它将为true。Fabric Node.js客户端Channel
对象中添加了新方法,以简化ChannelEventHub
对象的设置。使用如下命令获取将设置为与对等方基于通道的事件服务一块儿使用的ChannelEventHub
实例。ChannelEventHub
实例将使用对等实例正在使用的全部相同端点配置设置,例如tls证书以及主机和端口地址。
使用链接配置文件(请参阅参考资料)时,可使用节点的名称来获取新的通道事件路由。
var channel_event_hub = channel.newChannelEventHub('peer0.org1.example.com');
如下是在使用链接配置文件时如何获取通道事件路由列表的示例。如下内容将根据链接配置文件的当前活动客户端client
部分中定义的当前组织获取列表。组织中定义的将eventSource
设置为true的对象将添加到列表中。
var channel_event_hubs = channel.getChannelEventHubsForOrg();
建立节点实例时,可使用节点实例获取ChannelEventHub
实例。
let data = fs.readFileSync(path.join(__dirname, 'somepath/tlscacerts/org1.example.com-cert.pem')); let peer = client.newPeer( 'grpcs://localhost:7051', { pem: Buffer.from(data).toString(), 'ssl-target-name-override': 'peer0.org1.example.com' } ); let channel_event_hub = channel.newChannelEventHub(peer);
当须要监视要添加到分类账的新块时,请使用块事件侦听器。当新块被提交给节点上的分类账时,将通知Fabric客户端Node.js.而后,客户端Node.js将调用应用程序的已注册回调。回调将传递新添加的块的JSON表示。请注意,当未使用true值调用connect()
时,回调将接收过滤块。注册接收完整块的用户的访问权限将由节点的基于信道的事件服务检查。当须要查看先前添加的块时,回调的注册能够包括起始块号。回调将开始今后号码接收块,并在添加到分类账时继续接收新块。这是应用程序resume和replay在应用程序脱机时可能已丢失的事件的一种方法。应用程序应记住它已处理的最后一个块,以免replay整个分类账。
如下示例将注册块侦听器以开始接收块。
// keep the block_reg to unregister with later if needed block_reg = channel_event_hub.registerBlockEvent((block) => { console.log('Successfully received the block event'); <do something with the block> }, (error)=> { console.log('Failed to receive the block event ::'+error); <do something with the error> });
如下示例将使用起始块编号进行注册,由于此应用程序须要在特定块中恢复并replay丢失的块。应用程序回调将像当前事件同样处理同一区域中的replay块。块侦听器将继续接收块,由于它们已提交到节点的分类账。
// keep the block_reg to unregister with later if needed block_reg = channel_event_hub.registerBlockEvent((block) => { console.log('Successfully received the block event'); <do something with the block> }, (error)=> { console.log('Failed to receive the block event ::'+error); <do something with the error> }, {startBlock:23} );
如下示例将使用起始块编号和结束块进行注册。应用程序须要replay丢失的块。应用程序回调将处理与当前事件相同的区域中的replay块。当侦听器看到结束块事件时,块侦听器将自动取消注册,而且ChannelEventHub
将关闭。申请将没必要处理此句柄。
block_reg = channel_event_hub.registerBlockEvent((block) => { console.log('Successfully received the block event'); <do something with the block> }, (error)=> { console.log('Failed to receive the block event ::'+error); <do something with the error> }, // for block listeners, the defaults for unregister and disconnect are true, // so the they are not required to be set in the following example {startBlock:23, endBlock:30, unregister: true, disconnect: true} );
当须要监视组织对等方的交易完成时,请使用交易侦听器。当新块被提交给节点上的分类账时,将通知客户端Node.js.而后,客户端将检查块是否已注册的交易标识符。若是找到交易,则将经过交易ID,交易状态和块编号通知回调。过滤的块包含交易状态,所以无需链接到对等方的基于通道的事件服务便可接收完整的块。因为大多数非管理员用户将没法看到完整的块,所以当这些用户只须要监听其提交的交易时,链接到接收过滤的块将避免访问问题。
如下示例将显示在javascript承诺中注册交易ID并构建另外一个将交易发送到订购者的承诺。这两个承诺将一块儿执行,以便一块儿收到两个行动的结果。使用交易侦听器,取消注册的默承认选设置为true。所以,在如下示例中,在侦听器看到交易以后,将注册的侦听器将自动取消注册。
let tx_object = client.newTransactionID(); // get the transaction ID string for later use let tx_id = tx_object.getTransactionID(); let request = { targets : targets, chaincodeId: 'my_chaincode', fcn: 'invoke', args: ['doSomething', 'with this data'], txId: tx_object }; return channel.sendTransactionProposal(request); }).then((results) => { // a real application would check the proposal results console.log('Successfully endorsed proposal to invoke chaincode'); // start block may be null if there is no need to resume or replay let start_block = getBlockFromSomewhere(); let event_monitor = new Promise((resolve, reject) => { let handle = setTimeout(() => { // do the housekeeping when there is a problem channel_event_hub.unregisterTxEvent(tx_id); console.log('Timeout - Failed to receive the transaction event'); reject(new Error('Timed out waiting for block event')); }, 20000); channel_event_hub.registerTxEvent((event_tx_id, status, block_num) => { clearTimeout(handle); //channel_event_hub.unregisterTxEvent(event_tx_id); let the default do this console.log('Successfully received the transaction event'); storeBlockNumForLater(block_num); resolve(status); }, (error)=> { clearTimeout(handle); console.log('Failed to receive the transaction event ::'+error); reject(error); }, // when this `startBlock` is null (the normal case) transaction // checking will start with the latest block {startBlock:start_block} // notice that `unregister` is not specified, so it will default to true // `disconnect` is also not specified and will default to false ); }); let send_trans = channel.sendTransaction({proposalResponses: results[0], proposal: results[1]}); return Promise.all([event_monitor, send_trans]); }).then((results) => {
当须要监控将在您的链代码中发布的事件时,请使用链代码事件监听器。当新块提交到分类账时,将通知客户端Node.js.而后,客户端将在链代码事件的名称字段中检查已注册的链代码模式。监听器的注册包括用于检查链代码事件名称的正则表达式。若是发现链代码事件名称与侦听器的正则表达式匹配,则将经过链代码事件,块编号,交易ID和交易状态通知侦听器的回调。过滤的块将不具备链码事件有效载荷信息;它只有chaincode事件名称。若是须要有效载荷信息,则用户必须可以访问完整块,而且通道事件中心必须链接(true)以从对等方的基于通道的事件服务接收完整块事件。
如下示例演示如何在javascript承诺中注册链代码事件侦听器,并构建另外一个将交易发送到订购者的承诺。这两个承诺将一块儿执行,以便一块儿收到两个行动的结果。若是长期监视须要chaincode事件侦听器,请遵循上面的块侦听器示例。
let tx_object = client.newTransactionID(); let request = { targets : targets, chaincodeId: 'my_chaincode', fcn: 'invoke', args: ['doSomething', 'with this data'], txId: tx_object }; return channel.sendTransactionProposal(request); }).then((results) => { // a real application would check the proposal results console.log('Successfully endorsed proposal to invoke chaincode'); // Build the promise to register a event listener with the NodeSDK. // The NodeSDK will then send a request to the peer's channel-based event // service to start sending blocks. The blocks will be inspected to see if // there is a match with a chaincode event listener. let event_monitor = new Promise((resolve, reject) => { let regid = null; let handle = setTimeout(() => { if (regid) { // might need to do the clean up this listener channel_event_hub.unregisterChaincodeEvent(regid); console.log('Timeout - Failed to receive the chaincode event'); } reject(new Error('Timed out waiting for chaincode event')); }, 20000); regid = channel_event_hub.registerChaincodeEvent(chaincode_id.toString(), '^evtsender*', (event, block_num, txnid, status) => { // This callback will be called when there is a chaincode event name // within a block that will match on the second parameter in the registration // from the chaincode with the ID of the first parameter. console.log('Successfully got a chaincode event with transid:'+ txnid + ' with status:'+status); // might be good to store the block number to be able to resume if offline storeBlockNumForLater(block_num); // to see the event payload, the channel_event_hub must be connected(true) let event_payload = event.payload.toString('utf8'); if(event_payload.indexOf('CHAINCODE') > -1) { clearTimeout(handle); // Chaincode event listeners are meant to run continuously // Therefore the default to automatically unregister is false // So in this case we want to shutdown the event listener once // we see the event with the correct payload channel_event_hub.unregisterChaincodeEvent(regid); console.log('Successfully received the chaincode event on block number '+ block_num); resolve('RECEIVED'); } else { console.log('Successfully got chaincode event ... just not the one we are looking for on block number '+ block_num); } }, (error)=> { clearTimeout(handle); console.log('Failed to receive the chaincode event ::'+error); reject(error); } // no options specified // startBlock will default to latest // endBlock will default to MAX // unregister will default to false // disconnect will default to false ); }); // build the promise to send the proposals to the orderer let send_trans = channel.sendTransaction({proposalResponses: results[0], proposal: results[1]}); // now that we have two promises all set to go... execute them return Promise.all([event_monitor, send_trans]); }).then((results) => {
分享一些Fabric等区块链相关的交互式在线编程实战教程:
- Hyperledger Fabric 区块链开发详解,本课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通讯接口等核心概念,也包含Fabric网络设计、nodejs链码与应用开发的操做实践,是Nodejs工程师学习Fabric区块链开发的最佳选择。
- Hyperledger Fabric java 区块链开发详解,课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通讯接口等核心概念,也包含Fabric网络设计、java链码与应用开发的操做实践,是java工程师学习Fabric区块链开发的最佳选择。