Restate:支援JavaScript/Java的Rust低延遲持久工作流

banq發表於2024-06-13


Restate:使用持久的非同步/等待以容錯方式輕鬆構建工作流、事件驅動的應用程式和分散式服務。

  • 帶有 JS/Java/Kotlin 中的 SDK
  • 內建於 Rust/Tokio 中的輕量級執行時。

它是免費且開放的,SDK 是 MIT 許可的,執行時是 BSL

Restate擅長構建:

  • Lambda 工作流即程式碼
  • 事務性 RPC 處理程式
  • 使用 Kafka 進行事件處理

核心
Restate 為簡化應用程式開發提供的基本原語如下:

  • 可靠執行:使用者程式碼將始終執行至完成。中間失敗會導致重試,使用持久執行機制來恢復部分進度,而不會重複已執行的步驟。
  • 暫停使用者程式碼:長時間執行的使用者程式碼在等待承諾時暫停,並在該承諾得到解決時恢復。
  • 可靠通訊:使用者程式碼以精確一次的語義進行通訊。Restate 可靠地傳遞訊息,並將傳送者和接收者錨定在持久執行中,以確保不會發生丟失或重複。
  • 持久定時器:使用者程式碼可以休眠(和暫停)或者安排稍後的呼叫。
  • 隔離性:使用者程式碼可以被鍵入,這使得 Restate 安排它們遵循每個鍵單寫入器的語義。
  • 一致狀態:鍵控使用者程式碼可以附加鍵/值狀態,該狀態在呼叫期間被急切地推送到處理程式中,並在完成後寫回。這對於 FaaS 部署特別有效(有狀態無伺服器,耶!)。
  • 可觀察性和自省:Restate 會自動生成處理程式之間互動的開放遙測跟蹤,併為您提供 SQL shell 來查詢應用程式的分散式狀態。

特徵:

  • API 的核心設計目標是保持熟悉的風格。應用開發人員應該檢視 Restate 示例並說“嘿,這看起來很熟悉”。
  • 基本上每個操作(處理程式呼叫、步驟等)都經過共識層,以實現高度的彈性和一致性。
  • Restate 的執行時是一個獨立的二進位制檔案,除了持久磁碟外沒有其他依賴項。它基本上包含持久日誌、工作流狀態機、狀態儲存等的輕量級整合版本。這使得它非常緊湊,並且易於在膝上型電腦和伺服器上執行。
  • Restate 不僅為工作流實現持久執行,其核心構建塊是持久 RPC 處理程式(或事件處理程式)。它在持久執行的基礎上新增了一些概念,例如虛擬物件(將 RPC 處理程式轉變為虛擬參與者)、持久通訊和持久承諾。更多詳細資訊請參見:https://restate.dev/programming-model
  • 輕量級以日誌為中心的架構使 Restate 仍然具有良好的延遲:例如,對於 3 步持久工作流處理程式(在 EBS 上對每一步進行 fsync 重述),往返(從呼叫到結果)大約需要 50ms。


Restate:支援JavaScript/Java的Rust低延遲持久工作流


網友討論
1、作者是 Apache Flink 的原始建立者之一

2、作者swwen:
Flink Stateful Functions 是我們首次嘗試構建一個系統來滿足我們目前的用例。具體來說,在 Virtual Objects 中,你可以看到這一遺產。
藉助 Stateful Functions,我們很快意識到我們需要為事務構建一些東西,而 Flink 是為分析構建的。
這體現在很多方面,可能最明顯的是延遲:事務永續性在 Flink(檢查點間隔)中需要幾秒鐘,而在 Restate 中則需要幾毫秒。
此外,我們可以為 Restate 提供一個非常不同的開發環境,使其更相容現代應用程式開發。Flink 來自資料工程方面,具有非常不同的整合、工具等。

3、此類工具如何處理不斷變化的工作流程?例如,如果我有一個“持久工作流程”,它會休眠一個月,然後執行其下一個操作,如果我需要在該月內更改工作流程,我該怎麼辦?我真的很喜歡這個概念,但這似乎是除了相當短的工作流程之外的任何問題。如果我將資料和演算法分開,我可以在工作流程處於“活動”狀態時修改事件處理程式碼。

關鍵要點:

  1.  不可變程式碼平臺(如 Lambda)使事情變得更加容易處理: 舊程式碼“只要您的處理程式執行”即可執行,這是您所需要的屬性。這也可以在 Kubernetes 中使用一些巧妙的控制器來實現
  2.  透過這種方式延遲 RPC 和跨度時間的能力,您可以讓處理程式執行時間非常短,但執行時間卻非常長。這​​比在迴圈中反覆休眠要好得多 - 相反,您可以執行延遲尾部呼叫。

4、您能否分享設計工作流程時需要注意的限制的詳細資訊?我希望能夠一目瞭然地參考以下幾點:

  1. 工作流的最大執行時長
  2.  服務呼叫的最大輸入/輸出有效負載大小(以位元組為單位)
  3.  服務呼叫的最大超時時間
  4. 工作流中允許的最大狀態轉換次數
  5.  最大日誌歷史記錄保留時間

答:
  1. Restate 工作流沒有最大執行時長。使用 Restate,工作流只能執行幾秒鐘或跨越數月。對於長期執行的工作流,需要記住的一件事是,您可能必須在其生命週期內改進程式碼。這就是為什麼我們建議將它們編寫為延遲尾部呼叫序列(https://news.ycombinator.com/item?id=40659687
  2.  Restate 目前預設不嚴格限制輸入/輸出訊息的大小(但它可以選擇限制大小以保護系統)。不過,建議不要過分強調輸入/輸出大小,因為 Restate 需要將輸入訊息傳送到服務端點才能呼叫它。因此,輸入/輸出大小越大,呼叫服務處理程式並將結果傳送回使用者所需的時間就越長(增加延遲)。目前,只要訊息超過 10 MB,我們就會發出軟警告。
  3.  如果使用者沒有為其對 Restate 的呼叫指定超時,則系統不會超時。當然,對於長時間執行的呼叫,可能會發生外部客戶端失敗或其連線中斷的情況。在這種情況下,Restate 允許重新連線到正在進行的呼叫或檢索其結果(如果在此期間完成)。
  4.  Restate 對工作流狀態轉換的最大次數沒有限制。
  5.  只要呼叫/工作流正在進行,Restate 就會保留日誌歷史記錄。一旦工作流完成,我們將刪除日誌,但會保留完成的結果 24 小時。

補充:
您可以在 Restate 中儲存大量資料(工作流事件、步驟)。記錄的事件會快速移動到嵌入式 RocksDB,該資料庫在每個節點上都具有很高的可擴充套件性。該架構是分割槽的,雖然我們尚未完成所有多節點功能,但內部的所有內容都是以分割槽可擴充套件的方式構建的。

因此,問題不在於系統能做什麼,而在於你想要什麼:

  • - 如果您儲存了數萬個日記條目,重播可能需要一些時間。(附註:您也不需要這樣做,Restate 對顯式狀態的支援為您提供了一種直觀的替代方案,可以替代其他一些系統所推廣的“永遠執行無限日記”工作流模式。)
  • - 預設情況下,工作流的執行持續時間不受限制。更大的問題是,您希望將舊版本的業務邏輯例項保留多長時間?
  • - 歷史記錄保留(我們目前僅針對“工作流”型別的任務進行此操作),保留的量取決於您願意為儲存投入多少。RocksDB 非常適合讓舊資料沿 LSM 樹流動,而不會造成阻礙。

我們希望得到一些關於制定最佳預設設定的建議,因此我們很樂意在 Discord 上進行更多交流:https://discord.gg/skW3AZ6uGd

我認為我們唯一需要(並且已經)硬性限制的是訊息大小,因為如果您有許多處理程式處理非常大的訊息,這可能會對系統穩定性產生不利影響。這最終將需要一個功能,例如用於大訊息的帶外傳輸(例如,透過 S3)。

4、我不確定“In Rust”是否有任何營銷價值。產品的成功很少與程式語言的使用有關,甚至根本沒有關係。我理解 Paul Graham 關於程式語言有效性的論點,但對於工作流管理器而言,像我這樣的使用者根本不關心工作流系統使用哪種程式語言,即使我必須侵入系統內部,延遲實際上也比吞吐量重要得多。

答:最近我花了很多時間編寫 Rust,這對我來說是一個很大的缺點。
對於併發來說,這是一種糟糕的語言,並且傳遞依賴性可能會導致你經常無法恢復的恐慌。
這意味著整個生態系統就像坐在一顆即將爆炸的舊炸藥上。
JVM 確實已經證明自己是迄今為止高併發後端應用程式的最佳選擇。

5、我還沒有開始採用 Restate,但它已經在考慮中。Step Functions 可能比 Restate 更勝一籌的一點是,它可以將狀態機定義和執行歷史視覺化。能夠在概念層面而不是實施層面找到根本原因,這真的很棒。

Step Functions是無伺服器編排層,這是 Step Functions 最吸引人的部分之一。我們有許多大型工作流程,每天只執行幾次,需要幾個小時,還有一些短工作流程,執行時間不到一分鐘,在高峰時段執行更頻繁。即使經過多次狀態轉換,Step Functions 最終也非常具有成本效益,因為大多數時候,編排器處於空閒狀態。

6、看起來真的很棒。一直在尋找一些易於使用的非同步工作流 + cronjobs 服務,以便與 Vercel 等無伺服器一起使用。

7、我找不到與temporal 編解碼器伺服器類似的概念,temporal 會加密事件日誌中的所有資料。
目前,Restate 不支援此功能。由於 Restate 不需要訪問輸入/輸出訊息或狀態(它將其作為位元組傳送到服務端點),因此您可以新增自己的客戶端加密機制。在可預見的未來,Restate 可能會為其新增更整合的解決方案。

8、Restate 與temporal 比較:

  • (1) 據我所知,Restate 的延遲是 Temporal 無法實現的。Restate 的延遲較低,因為 (a) 它的事件日誌架構和 (b) Restate 不需要為活動生成任務,而是呼叫 RPC 處理程式。
  • (2) Restate 與 FaaS 配合得非常好。FaaS 本質上需要一個“推送事件”模型,這正是 Restate 所做的(推送事件、呼叫處理程式)。如果我沒記錯的話,Temporal 有一個拉取任務的工作者模型,而拉取模型對 FaaS 來說並不好。Restate + AWS Lambda 實際上是一個很棒的任務佇列,您可以超快速地提交任務,並且可以幾乎無限地自動擴充套件其工作者(Lambda)。
  • (3) Restate 是一個獨立的二進位制檔案,你下載並啟動它就大功告成了。我認為這與大多數系統(不僅僅是 Temporal)的體驗截然不同。為什麼應用程式開發人員如此喜歡 Redis,儘管它的耐用性值得商榷?我認為這是他們喜歡的超輕量級方式,而這正是我們想要複製的(儘管具有適當的耐用性)。
  • (4) 也許最重要的是,Restate 的功能遠不止工作流。你可以將它僅用於工作流,但也可以實現持久通訊(恰好一個 RPC)、以參與者風格的方式維護狀態(透過虛擬物件)或從 Kafka 提取事件的服務。

這可能不是您構建的第一件事,但它向您展示瞭如果您願意的話您可以走多遠:它是一個完整的應用程式,具有許多服務、工作流、數字孿生,其中一些連線到 Kafka。https ://github.com/restatedev/examples/tree/main/end-to-end-...

所有執行和通訊都是非同步、持久、可靠的。我認為使用 Temporal 構建這種應用程式非常困難,如果你構建它,你可能會使用一些非常奇怪的訊號怪癖,例如在構建數字孿生的狀態維護時,這不會讓其他任何應用程式開發人員覺得非常直觀。

9、Restate自帶資料層的理由:

傾向於考慮在狀態機中構建服務:每個重要步驟都在安全的地方進行跟蹤,並透過狀態機進行狀態轉換。如果手動執行此操作,您將聯絡 DBMS 並在發生重要事件時明確檢查您的狀態。

為了實現冪等性,您最終會在程式碼中加入準備提交型別的步驟,首先讀取儲存的狀態,然後在每個邏輯步驟中決定是恢復之前的部分執行還是重新開始。這很快就會過時,因此大多數程式碼最終可能依賴於開始時的一次冪等性檢查,然後呼叫者重試。您還需要一個外部任務佇列或某種型別的清除程式來拾取並重新驅動部分完成的執行。

像 Restate 這樣完整的專用系統的優點在於,它為您提供了專為跟蹤執行任務而設計的持久日誌服務,並且還為您提供了一個 SDK,使您可以輕鬆實現“冪等塊鏈”效果,而無需手動操作巨大的狀態機。

您不必使用 Restate 來儲存資料,儘管您可以這樣做 - 並且您可以獲得在日誌記錄過程中使用相同隔離屬性自動提交狀態更改的好處。但是,您可以輕鬆地將寫入操作編排到外部儲存(例如 RDBMS、KV、佇列)中,並使用與 Restate 服務其餘部分相同的保證進度語義。它的執行語義使這變得更容易、更愉快,因為您可以立即重試。

最後,值得一提的是,我們公開了一個與 PostgreSQL 協議相容的 SQL 查詢端點。這允許您查詢您選擇儲存在 Restate 中的任何狀態以及服務後設資料,即反映活動呼叫。

10、Restate 與 Apache Airflow 或 Prefect 相比如何?

  • 一個區別是 Airflow 似乎更適合更繁重的操作,例如資料管道。相比之下,Restate 預設不會生成任何任務,但它更像是 RPC 或事件處理程式的代理/代理,並新增了持久重試、日誌記錄、建立持久 RPC 的能力等。這使得它非常輕量級:如果處理程式在執行的容器中速度很快,則整個過程會導致超快的週轉時間(毫秒)。
  • 另一個區別在於邏輯的定義方式、可維持狀態、可對其他處理程式進行一次呼叫。

11、令人興奮的工具讓使用微服務變得更加容易。

12、處理 RPC 的永續性是一個好主意。您可以進行鏈式回滾嗎?即,呼叫堆疊下的 RPC 失敗,而不是重試,而是恢復整個堆疊?
這是示例倉庫中另一個執行補償的示例。還有一個 Java 示例 https://github.com/restatedev/examples/blob/main/basics/basi...

13、Restate 殺手級功能:

  • 能夠完全隱藏持久執行,讓使用者無法看到,方法是能夠在任何時間點拍攝執行快照,然後透明地將其記錄在引擎中。

現在假設存在這樣的語言,並且它也可以相當快地拍攝這些快照,但確定在哪裡拍攝快照在邏輯上是安全的,以及何時執行無法繼續,因為您需要等待儲存結果的確認,這仍然是一個很大的問題。例如,假設您有以下程式碼:

val 結果A = 呼叫A()
val 結果B = 呼叫B(結果A)

A 和 B 都會執行一些非確定性操作,例如,它們會向其他系統執行 HTTP 呼叫。假設 callB() 完成後,但在得到 HTTP 響應之前,你的程式碼由於某種原因崩潰了。

如果你在 callA() 和 callB() 之間沒有拍攝任何快照,那麼你將永遠完全丟失 B 被呼叫 resultA 的事實,而當你下一次重新執行 A 時,它生成的結果可能與第一次生成的結果不同。

由於這個問題,你仍然需要以某種方式手動定義一些 "安全點",以便安全地拍攝這些快照。這意味著我們無法真正向使用者隱藏持久執行,因為你仍然需要一些類似於 "snapshot_here "的語句來告訴引擎在哪裡拍攝快照是安全的。

在我們的 Restate  SDK 中,我們有效地實現了這一點,採取的安全方法是在連續執行兩個 ctx.run() 時始終等待儲存確認。

14、設計靈感:
Restate 構建為分片複製狀態機,類似於

  • TiKV ( https://tikv.org/<a> )、
  • Kudu ( https://kudu.apache.org/kudu.pdf )
  • 或 CockroachDB ( https://github.com/cockroachdb/cockroach )

的設計方式。

我們不依賴於特定的共識實現,而是決定將此部分封裝到虛擬日誌中(受 Delos https://www.usenix.org/system/files/osdi20-balakrishnan.pdf啟發),因為這樣可以更輕鬆地針對不同的部署場景(本地、雲、經濟高效的 blob 儲存)調整系統。

此外,它還允許其他一些很酷的功能,例如無縫地從一種日誌實現移動到另一個日誌實現。

除此之外,整個系統設計還受到流處理系統(如 Apache Flink ( https://flink.apache.org/<a> )、日誌儲存系統(如 LogDevice ( https://logdevice.io/<a> ) )等)等思想的影響。

這是設計理念的混合體。LogDevice(免責宣告,我是 LogDevice 設計師之一)和 Delos(Bifrost,我們的分散式日誌設計)肯定有靈感。您可以在https://www.usenix.org/system/files/osdi20-balakrishnan.pdf 中閱讀有關 Delos 的資訊

相關文章