FabricSDK相关

整理一些看SDK实现时候有意思的代码

在Fabric的SDK的新的版本,引入了Gateway在后续版本中建议使用此模块来访问Fabric网络,因为这个库简化了高可以的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// fabric-network/lib/gateway.js

constructor() {
this.options = {
queryHandlerOptions: {
strategy: QueryStrategies.MSPID_SCOPE_SINGLE
},
eventHandlerOptions: {
commitTimeout: 300, // 5 minutes 这个是修改SDK等待超时行为。
strategy: EventStrategies.MSPID_SCOPE_ALLFORTX
},
discovery: {
enabled: Client.getConfigSetting('initialize-with-discovery', true)
},
checkpointer: {
factory: CheckpointFactories.FILE_SYSTEM_CHECKPOINTER,
options: {}
},
eventHubSelectionOptions: {
strategy: EventHubSelectionStrategies.MSPID_SCOPE_ROUND_ROBIN,
}
};
}

// 其中QueryStrategies.MSPID_SCOPE_SINGLE
/**
* @typedef DefaultQueryHandlerStrategies
* @memberof module:fabric-network
* @property {function} MSPID_SCOPE_SINGLE Query any one of the event hubs for the connected organisation. Continue
* to use the same event hub for all queries unless it fails. 查询通过所连接组织的任何一个活动节点。每次查询都使用相同的节点,除非发生查询失败。
* @property {function} MSPID_SCOPE_ROUND_ROBIN Query any one of the event hubs for the connected organisation.
* Use the next available peer for each successive query. 查询通过所连接组织的任何一个活动节点。在每次成功查询之后使用下一个有效的节点。
*/

module.exports = {
MSPID_SCOPE_SINGLE,
MSPID_SCOPE_ROUND_ROBIN
};

// 事件策略,默认是等待同一个组织下的所有节点响应事件。
/**
* @typedef DefaultEventHandlerStrategies
* @memberof module:fabric-network
* @property MSPID_SCOPE_ALLFORTX Listen for transaction commit events from all peers in the client identity's
* organization. 侦听来自客户端标识组织中所有对等方的事务提交事件。
* The [submitTransaction]{@link module:fabric-network.Contract#submitTransaction} function will wait until successful
* events are received from <em>all</em> currently connected peers (minimum 1). 函数将等待,直到从所有当前连接的对等方接收到成功的事件(至少1个)。
* @property MSPID_SCOPE_ANYFORTX Listen for transaction commit events from all peers in the client identity's
* organization.
* The [submitTransaction]{@link module:fabric-network.Contract#submitTransaction} function will wait until a
* successful event is received from <em>any</em> peer. 函数将一直等到从任何对等方接收到成功的事件。
* @property NETWORK_SCOPE_ALLFORTX Listen for transaction commit events from all peers in the network.
* 侦听来自网络中所有对等方的事务提交事件。
* The [submitTransaction]{@link module:fabric-network.Contract#submitTransaction} function will wait until successful
* events are received from <em>all</em> currently connected peers (minimum 1). 函数将等待,直到从所有当前连接的对等方接收到成功的事件(至少1个)。
* @property NETWORK_SCOPE_ANYFORTX Listen for transaction commit events from all peers in the network.
* The [submitTransaction]{@link module:fabric-network.Contract#submitTransaction} function will wait until a
* successful event is received from <em>any</em> peer. 函数将一直等到从任何对等方接收到成功的事件。
*/

module.exports = {
MSPID_SCOPE_ALLFORTX,
MSPID_SCOPE_ANYFORTX,
NETWORK_SCOPE_ALLFORTX,
NETWORK_SCOPE_ANYFORTX
};
1
2
3
4
5
6
7
8
9
10
11
/**
* Handles events for a given transaction. Used to wait for a submitted transaction to be successfully commited to
* the ledger.
* Delegates to an event strategy to decide whether events or errors received should be interpreted as success or
* failure of a transaction.
* 委托给事件策略,以决定接收到的事件或错误是应解释为事务成功还是事务失败。
* @private
*/
class TransactionEventHandler {

}

区块链交互本质上和网络交互存在一个相同的问题,当发生在区块链层面的数据提交发生成功或者失败的时候。
业务层面,应该如何避免数据不一致的问题。
例如:
A程序,发出一个请求。
B程序开始处理。
A程序崩溃。
A程序重启后,如何处理在崩溃瞬间的任务处理策略,是应该重试还是修正。
联盟链本质上存在一个问题,CAP问题,没有事务,如何解决最终一致性问题,并且有效的返回前端用户正确的响应,是我们应该考虑和解决的问题。
传统的分布式事务和区块链存在差异,传统事务是可以重试,在业务过程失败, 区块链执行成功的情况,理论上是不应该重试,重试会导致区块链网络存在新的交易记录。

整个本质上还是分布式事务处理问题

CAP在区块链上的问题,个人觉得使用ebay的方案就可以了。支付宝DTS方案,基于2PC模式肯定也不适合区块链场景。

蘑菇街的方案个人感觉比较适合常规的分布式事务的处理,理解起来比较简单,不过在真实的场景实现应该还有很多细节问题。

执行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Generate Blockchain Transaction Id

Begin Transaction
Insert Into TransactionRecord (Transaction Id, Transaction Data)
Queue Message (Transaction Id, Transaction Data)
End Transaction

Result = Execte Blockchain Commit

If Result Failed
# 如果发生失败,移除队列信息。
Begin Transaction
Remove Queue Transaction Id
return Failed Message
End Transaction
Else
Begin Transaction
Get Other Data Form Query Result
Insert Into Other Data To DB
Remove Queue Transaction Id
return Success Message
End Transaction
End

重启后执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
For each message in queue

Peek message

# 区块链网络上查询对应的区块ID。
Execte Blockchain Query Transaction ID

# 存在表示交易执行成功
If Exist
Begin Transaction
Get Other Data Form Query Result
Insert Into Other Data To DB
Remove Queue Transaction Id
End Transaction
# 这里不在执行重试。
Else
Begin Transaction
Remove Queue Transaction Id
End Transaction
End If
End for

浏览器接口行为,当多台负载的情况下,一个崩溃后会导致数据重试到其它服务器,这个时候区块链层面的交易ID是不同的。
因此,不能在崩溃重启的时候,直接重写旧的交易数据,因此区块链只兜底,在区块链数据已经写入的情况,中心化数据库没有写入时的数据一致。

基于用户会话的接口控制,必须保证用户同时只能登录一个地方,这个有可能不符合需求,比如需要浏览器,手机同时登录,这个时候,如果不是公用Token的处理模式,就会导致存在问题。

  • 执行太久导致重试,并且请求定向负载均衡的其它服务。
  • 执行请求时候,服务崩溃,定向到其它服务再次执行,导致区块链层面数据执行2次。

解决在多台服务负载的情况下,保证数据一致性

分布式事务问题

分布式事务问题, 可能是金融系统最难处理的问题之一了

其它

CAP原则, 在分布式系统中, 一致性(Consistency)、可用性(Avaliability)、分区容错性(Partition Tolerance), CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

  • P 分区容错性, 分布式系统的多个节点之间, 必然可能出现无法通信的情况, 因此在分布式系统中必定只能是CP或者AP
  • 一致性和可用性是相互冲突的, 如果要一致, 在更改一个节点的数据的时候, 必须同步修改其它节点的数据, 这个过程中, 其它节点是无法读取和写入就违背了可用性原则.

因此, 在分布式系统中都不追求强一致性, 只需要保证最终所有节点的数据一致即可.

ACID数据库在写入和更新资料的时候保证事务的正确可靠必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。

  • Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。