Corda 学习

R3 Corda: 升级 CorDapp(非平台版本升级)- F

2018-09-05  本文已影响0人  李甲川

原文地址:https://docs.corda.net/upgrading-cordapps.html#flow-versioning

任何初始化其他 flows 的 flow 必须要使用 @InitiatingFlow 注解,像下边这样定义:

annotation class InitiatingFlow(val version: Int = 1)

version 属性默认值为1,定义了 flow 的版本。当flow 有任何一个新的 release 的时候并且这个 release 包含的变动是非向下兼容的,这个数值应该增加。一个非向下兼容的改动是一个改变了 flow 的接口的变动。

Flow 的接口是如何定义的?

Flow 的接口是通过在 InitiatingFlowInitiatedBy flow 之间有序的 sendreceive 调用来定义的,包括发送和接受的数据的类型。我们可以将 flow 的接口如下图这样表示:

Flow 接口
在上边的图中,InitiatingFlow

哪些是非向下兼容的改动?

Flow 可以有两种主要的方式会变为非向下兼容的:

当运行不兼容版本的 flows 会发生什么?

带有非兼容接口的 InitiatingFlowInitiatedBy flows 可能会出现下边的行为:

我应该如何升级我的 flows?

  1. 更新 flow 并且测试。在 InitiatingFlow 注解中增加 flow 版本号。
  2. 确保已经存在的所有版本的 flow 已经运行完了并且没有未结束的 SchedulableFlows 在网络中的任何节点中。这个可以通过清理节点的方式来实现,接下来会讲到。
  3. 关闭节点
  4. 用包含新的 flow 的 CorDapp JAR 文件替换掉原来的 CorDapp JAR
  5. 启动节点

如果你关掉了所有的节点并且同时更新了他们的 flow 的话,可能产生任何的不兼容的改动。

对于一些节点可能仍旧继续运行某个 flow 的以前版本的情况,这样你的新版本 flow 可能会跟一个旧版本进行沟通,更新的 flows 需要具备向下兼容性。这可能是任何真正的部署中都会发生的问题,你可能不会很容易地去在整个网络中去协调推出一个新的 code。

我该如何确保 flow 的向下兼容性?

InitiatingFlow 版本号会被包含在 flow session handshake 中并且通过 FlowLogic.getFlowContext 方法暴露给双方。这个方法需要一个 Party 作为输入,然后会返回一个 FlowContext 对象,这个对象描述了在对方节点上正在运行的 flow。它含有一个 flowVersion 的属性,可以使用这个属性来在不同的 flow 版本间来定制你自己的 flows,例如:

@Suspendable
override fun call() {
    val otherFlowVersion = otherSession.getCounterpartyFlowInfo().flowVersion
    val receivedString = if (otherFlowVersion == 1) {
        otherSession.receive<Int>().unwrap { it.toString() }
    } else {
        otherSession.receive<String>().unwrap { it }
    }
}

上边的代码演示了当 flow 的第一个版本期望收到一个 Int,但是后续的版本变成了期望收到一个 String。这个 flow 在跟其他仍然运行着包含旧的 flow 的旧的 CorDapp 之间还是能够进行沟通的。

我该如何处理关于 in-lined subflows 的接口变化?

下边是一个 in-lined subflow:

@StartableByRPC
@InitiatingFlow
class FlowA(val recipient: Party) : FlowLogic<Unit>() {
    @Suspendable
    override fun call() {
        subFlow(FlowB(recipient))
    }
}

@InitiatedBy(FlowA::class)
class FlowC(val otherSession: FlowSession) : FlowLogic() {
    // Omitted.
}

// Note: No annotations. This is used as an inlined subflow.
class FlowB(val recipient: Party) : FlowLogic<Unit>() {
    @Suspendable
    override fun call() {
        val message = "I'm an inlined subflow, so I inherit the @InitiatingFlow's session ID and type."
        initiateFlow(recipient).send(message)
    }
}

In-lined subflows 是当跟对方初始一个新的 flow session 的时候被调用的 flows。假设 flow A 调用 in-lined subFlow BB 初始了一个跟对方的会话(session)。对方使用的 FlowLogic 类型决定应该调用哪个对应的 flow 应该是由 A 决定的,而不是 B。这意味着 in-lined flow 的 response logic 必须要在 InitiateBy flow 里被显式地实现。这个可以通过调用一个匹配的 in-lined counter-flow,或者在对方的被初始的父的 flow 中显式地实现。In-lined subflows 也会从他们的父 flow 中继承 session IDs。

因此,一个 in-lined subflow 的一个借口的改动必须要考虑对父 flow 接口也要有一个改动。

一个 in-lined subflow 的例子是 CollectSignaturesFlow。他有一个没有 InitiateBy 注解的 response 的 flow 叫 SignTransactionFlow。这是因为这两个 flows 都是 in-lined。这两个 flows 是如何彼此交流的是通过调用他们的父 flows 来定义的。

在代码中,in-lined subflows 看起来就是一个常规的 FlowLogic 的实例,但是没有 InitiatingFlow 或者 InitiatedBy 注解。

In-lined subflows 是没有版本的,因为他们的版本是继承于他们的父 flow 的(InitiatingFlowInitiatedBy)。

不是 InitiatingFlow 或者 InitiatedBy flow,也不是由一个 InitiatingFlow 或者 InitiatedBy flow 调用的 in-lined subflows ,更新的时候可以不考虑向下兼容的问题。这种类型的 flows 包括用来查询 vault 的 utility flows,或者对外部系统进行查询的 flows。

上一篇下一篇

猜你喜欢

热点阅读