Hyperledger Composer中如何规范化资源ID的编排?
实现ourAsset ID自动递增的方案
刚好之前在Hyperledger Fabric项目里处理过类似的自增ID需求,给你分享两种可行的实现方案,分别适用于不同场景:
方案一:直接读取资产注册表取最大ID(适合低并发、资产量不大的场景)
这个思路很直接:每次创建新资产时,读取整个ourAsset注册表,把所有资产按ID降序排序,取最大的那个ID加1作为新ID。如果注册表是空的,就从1开始。
示例交易代码
/** * 创建带自增ID的ourAsset * @param {org.biznet.CreateOurAsset} tx - 自定义的创建资产交易 * @transaction */ async function createOurAsset(tx) { const factory = getFactory(); // 获取ourAsset的资产注册表 const assetRegistry = await getAssetRegistry('org.biznet.ourAsset'); // 读取所有资产记录 const allAssets = await assetRegistry.getAll(); let newAssetId = 1; // 默认初始ID if (allAssets.length > 0) { // 按ID降序排序(注意要把字符串ID转成数字比较,避免字符串排序的问题) allAssets.sort((assetA, assetB) => parseInt(assetB.assetID) - parseInt(assetA.assetID)); // 取排序后的第一条,也就是ID最大的资产 const latestAsset = allAssets[0]; newAssetId = parseInt(latestAsset.assetID) + 1; } // 创建新资产,用自增后的ID const newAsset = factory.newResource('org.biznet', 'ourAsset', newAssetId.toString()); // 这里可以给资产设置其他属性,比如从交易参数里获取 // newAsset.name = tx.assetName; // newAsset.owner = tx.assetOwner; // 将新资产添加到注册表 await assetRegistry.add(newAsset); }
注意点
- 这种方式的问题在于并发场景下可能出现ID重复:如果两个交易同时读取注册表,拿到同一个最大ID,就会生成重复的新ID。所以只适合低并发的场景。
- 如果资产数量很大,
getAll()会拉取所有数据,性能会受影响。
方案二:使用独立计数器资产(适合高并发、资产量大的场景)
这个方案更可靠,核心是创建一个专门的Counter资产来存储当前ourAsset的最大ID,每次创建新资产时,先更新计数器的数值,再用这个数值作为新ID。利用Fabric的乐观锁机制来避免并发冲突。
第一步:定义Counter资产类型
首先在你的CTO模型文件里添加这个资产:
namespace org.biznet asset Counter identified by counterId { o String counterId // 计数器唯一标识,比如固定为"ourAssetCounter" o Integer currentValue // 当前最大ID值 } // 你的ourAsset资产定义 asset ourAsset identified by assetID { o String assetID // 其他属性... } // 创建资产的交易定义 transaction CreateOurAsset { // 可以添加需要传递的参数,比如资产名称、所有者等 // o String assetName // o Participant owner }
第二步:编写交易代码
/** * 创建带自增ID的ourAsset(基于计数器方案) * @param {org.biznet.CreateOurAsset} tx - 创建资产的交易 * @transaction */ async function createOurAsset(tx) { const factory = getFactory(); const counterRegistry = await getAssetRegistry('org.biznet.Counter'); const assetRegistry = await getAssetRegistry('org.biznet.ourAsset'); // 获取或初始化计数器 let counter; try { counter = await counterRegistry.get('ourAssetCounter'); } catch (err) { // 如果计数器不存在,初始化它,初始值设为0 counter = factory.newResource('org.biznet', 'Counter', 'ourAssetCounter'); counter.currentValue = 0; await counterRegistry.add(counter); } // 自增计数器的值 counter.currentValue += 1; const newAssetId = counter.currentValue.toString(); // 更新计数器(这一步会触发Fabric的乐观锁检查,如果有并发修改会抛出冲突异常) await counterRegistry.update(counter); // 创建新的ourAsset const newAsset = factory.newResource('org.biznet', 'ourAsset', newAssetId); // 设置资产的其他属性 // newAsset.name = tx.assetName; // newAsset.owner = tx.owner; // 添加到资产注册表 await assetRegistry.add(newAsset); }
优势
- 避免并发冲突:当两个交易同时修改计数器时,Fabric的乐观锁会检测到冲突,其中一个交易会抛出
ConflictException,客户端可以捕获这个异常并重试交易,保证ID不会重复。 - 性能更好:不需要拉取所有资产记录,只需要操作一个计数器资产,效率更高。
最后补充
如果你的场景并发不高,方案一足够简单好用;如果是生产环境高并发场景,强烈推荐方案二,它能更可靠地保证ID的唯一性。
内容的提问来源于stack exchange,提问作者tahaf10
相关产品推荐
相关产品推荐

