R3 Corda: 升級(jí) CorDapp(非平臺(tái)版本升級(jí))- Flow 版本

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

任何初始化其他 flows 的 flow 必須要使用 @InitiatingFlow 注解,像下邊這樣定義:

annotation class InitiatingFlow(val version: Int = 1)

version 屬性默認(rèn)值為1,定義了 flow 的版本。當(dāng)flow 有任何一個(gè)新的 release 的時(shí)候并且這個(gè) release 包含的變動(dòng)是非向下兼容的,這個(gè)數(shù)值應(yīng)該增加。一個(gè)非向下兼容的改動(dòng)是一個(gè)改變了 flow 的接口的變動(dòng)。

Flow 的接口是如何定義的?

Flow 的接口是通過(guò)在 InitiatingFlowInitiatedBy flow 之間有序的 sendreceive 調(diào)用來(lái)定義的,包括發(fā)送和接受的數(shù)據(jù)的類型。我們可以將 flow 的接口如下圖這樣表示:

Flow 接口

在上邊的圖中,InitiatingFlow

  • 發(fā)送了一個(gè) Int
  • 接收了一個(gè) String
  • 發(fā)送了一個(gè) String
  • 接收了一個(gè) CustomType
    InitiatedBy flow 恰恰相反:
  • 接收了一個(gè) Int
  • 發(fā)送了一個(gè) String
  • 接收了一個(gè) String
  • 發(fā)送了一個(gè) CustomType
    只要 IntiatingFlowInitiatedBy flows 遵循這個(gè)有序的一系列的動(dòng)作,那么 flows 就可以按照任何你覺(jué)得合適的方式來(lái)實(shí)現(xiàn)(包括添加不共享給其他節(jié)點(diǎn)的業(yè)務(wù)邏輯)。

哪些是非向下兼容的改動(dòng)?

Flow 可以有兩種主要的方式會(huì)變?yōu)榉窍蛳录嫒莸模?/p>

  • sendreceive 調(diào)用的順序變化:
    1. 一個(gè) send 或者 receiveInitiatingFlow 或者 InitiatedBy flow 中被添加或者刪除了
    2. sendreceive 調(diào)用的順序變了
  • sendreceive 調(diào)用的類型變了

當(dāng)運(yùn)行不兼容版本的 flows 會(huì)發(fā)生什么?

帶有非兼容接口的 InitiatingFlowInitiatedBy flows 可能會(huì)出現(xiàn)下邊的行為:

  • flows 會(huì)沒(méi)有明確原因地停住了并且永遠(yuǎn)也不會(huì)終止,通常是因?yàn)橐粋€(gè) flow 在等待這著一個(gè)回復(fù),但是這個(gè)回復(fù)永遠(yuǎn)不會(huì)從另一方返回來(lái)
  • 其中的一個(gè) flow 會(huì)帶有異常地結(jié)束:“Expected Type X but Received Type Y”,因?yàn)?send 或者 receive 類型不正確
  • 其中的一個(gè) flow 會(huì)帶有異常地結(jié)束:“Counterparty flow terminated early on the other side”,因?yàn)橐粋€(gè) flow 向另外一個(gè) flow 發(fā)送了一些數(shù)據(jù),但是后邊這個(gè) flow 已經(jīng)結(jié)束了

我應(yīng)該如何升級(jí)我的 flows?

  1. 更新 flow 并且測(cè)試。在 InitiatingFlow 注解中增加 flow 版本號(hào)。
  2. 確保已經(jīng)存在的所有版本的 flow 已經(jīng)運(yùn)行完了并且沒(méi)有未結(jié)束的 SchedulableFlows 在網(wǎng)絡(luò)中的任何節(jié)點(diǎn)中。這個(gè)可以通過(guò)清理節(jié)點(diǎn)的方式來(lái)實(shí)現(xiàn),接下來(lái)會(huì)講到。
  3. 關(guān)閉節(jié)點(diǎn)
  4. 用包含新的 flow 的 CorDapp JAR 文件替換掉原來(lái)的 CorDapp JAR
  5. 啟動(dòng)節(jié)點(diǎn)

如果你關(guān)掉了所有的節(jié)點(diǎn)并且同時(shí)更新了他們的 flow 的話,可能產(chǎn)生任何的不兼容的改動(dòng)。

對(duì)于一些節(jié)點(diǎn)可能仍舊繼續(xù)運(yùn)行某個(gè) flow 的以前版本的情況,這樣你的新版本 flow 可能會(huì)跟一個(gè)舊版本進(jìn)行溝通,更新的 flows 需要具備向下兼容性。這可能是任何真正的部署中都會(huì)發(fā)生的問(wèn)題,你可能不會(huì)很容易地去在整個(gè)網(wǎng)絡(luò)中去協(xié)調(diào)推出一個(gè)新的 code。

我該如何確保 flow 的向下兼容性?

InitiatingFlow 版本號(hào)會(huì)被包含在 flow session handshake 中并且通過(guò) FlowLogic.getFlowContext 方法暴露給雙方。這個(gè)方法需要一個(gè) Party 作為輸入,然后會(huì)返回一個(gè) FlowContext 對(duì)象,這個(gè)對(duì)象描述了在對(duì)方節(jié)點(diǎn)上正在運(yùn)行的 flow。它含有一個(gè) flowVersion 的屬性,可以使用這個(gè)屬性來(lái)在不同的 flow 版本間來(lái)定制你自己的 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 }
    }
}

上邊的代碼演示了當(dāng) flow 的第一個(gè)版本期望收到一個(gè) Int,但是后續(xù)的版本變成了期望收到一個(gè) String。這個(gè) flow 在跟其他仍然運(yùn)行著包含舊的 flow 的舊的 CorDapp 之間還是能夠進(jìn)行溝通的。

我該如何處理關(guān)于 in-lined subflows 的接口變化?

下邊是一個(gè) 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 是當(dāng)跟對(duì)方初始一個(gè)新的 flow session 的時(shí)候被調(diào)用的 flows。假設(shè) flow A 調(diào)用 in-lined subFlow B,B 初始了一個(gè)跟對(duì)方的會(huì)話(session)。對(duì)方使用的 FlowLogic 類型決定應(yīng)該調(diào)用哪個(gè)對(duì)應(yīng)的 flow 應(yīng)該是由 A 決定的,而不是 B。這意味著 in-lined flow 的 response logic 必須要在 InitiateBy flow 里被顯式地實(shí)現(xiàn)。這個(gè)可以通過(guò)調(diào)用一個(gè)匹配的 in-lined counter-flow,或者在對(duì)方的被初始的父的 flow 中顯式地實(shí)現(xiàn)。In-lined subflows 也會(huì)從他們的父 flow 中繼承 session IDs。

因此,一個(gè) in-lined subflow 的一個(gè)借口的改動(dòng)必須要考慮對(duì)父 flow 接口也要有一個(gè)改動(dòng)。

一個(gè) in-lined subflow 的例子是 CollectSignaturesFlow。他有一個(gè)沒(méi)有 InitiateBy 注解的 response 的 flow 叫 SignTransactionFlow。這是因?yàn)檫@兩個(gè) flows 都是 in-lined。這兩個(gè) flows 是如何彼此交流的是通過(guò)調(diào)用他們的父 flows 來(lái)定義的。

在代碼中,in-lined subflows 看起來(lái)就是一個(gè)常規(guī)的 FlowLogic 的實(shí)例,但是沒(méi)有 InitiatingFlow 或者 InitiatedBy 注解。

In-lined subflows 是沒(méi)有版本的,因?yàn)樗麄兊陌姹臼抢^承于他們的父 flow 的(InitiatingFlowInitiatedBy)。

不是 InitiatingFlow 或者 InitiatedBy flow,也不是由一個(gè) InitiatingFlow 或者 InitiatedBy flow 調(diào)用的 in-lined subflows ,更新的時(shí)候可以不考慮向下兼容的問(wèn)題。這種類型的 flows 包括用來(lái)查詢 vault 的 utility flows,或者對(duì)外部系統(tǒng)進(jìn)行查詢的 flows。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • feisky云計(jì)算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 4,269評(píng)論 0 5
  • 我真正意義上完整看完的第一部韓劇,去年這個(gè)時(shí)候就看完了,直到今天偶然間又聽(tīng)到了劇中的插曲,勾起了我全部的回憶...
    魚(yú)嚎閱讀 360評(píng)論 0 1
  • 如果有一天,可以選擇,我定會(huì)選擇遠(yuǎn)走高飛,遠(yuǎn)走他鄉(xiāng),我們不必活在別人的眼里,卻時(shí)時(shí)活在別人的眼中。 ...
    hello橙閱讀 632評(píng)論 0 1
  • 姜廣平老師不光光是一位優(yōu)秀的作家,還是一位優(yōu)秀的文學(xué)評(píng)論家。 說(shuō)到文學(xué)評(píng)論,我還是在上大學(xué)的時(shí)候第一次聽(tīng)說(shuō)。當(dāng)時(shí),...
    上官飛鴻閱讀 1,883評(píng)論 29 40

友情鏈接更多精彩內(nèi)容