FAkka.Proc.Supervisor 1.564.101.201

dotnet add package FAkka.Proc.Supervisor --version 1.564.101.201
                    
NuGet\Install-Package FAkka.Proc.Supervisor -Version 1.564.101.201
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FAkka.Proc.Supervisor" Version="1.564.101.201" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FAkka.Proc.Supervisor" Version="1.564.101.201" />
                    
Directory.Packages.props
<PackageReference Include="FAkka.Proc.Supervisor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FAkka.Proc.Supervisor --version 1.564.101.201
                    
#r "nuget: FAkka.Proc.Supervisor, 1.564.101.201"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FAkka.Proc.Supervisor@1.564.101.201
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FAkka.Proc.Supervisor&version=1.564.101.201
                    
Install as a Cake Addin
#tool nuget:?package=FAkka.Proc.Supervisor&version=1.564.101.201
                    
Install as a Cake Tool

Akka.Proc.Supervisor

Akka.Proc.Supervisor 是一個基於 Akka.NET 的進程管理與監督服務,專門用來管理與調度外部進程 (Processes),特別是 F# Interactive (FSI) 節點。它結合了 Akka Cluster Sharding、Quartz.NET 排程與 Suave HTTP REST API,提供了一個高可用、可擴展的分散式進程執行與監控環境。

系統架構

系統主要分為兩種執行模式 (Mode):Supervisor (監督者)ProcNode (進程節點)

1. Supervisor (監督者模式)

負責管理所有衍生的子進程。核心元件包括:

  • ProcRegistryActor: 維護所有子進程的狀態快照 (Snapshot),提供進程列表與狀態查詢。
  • ProcSupervisorActor: 作為對外的唯一入口,將指令路由至對應的 Sharding Region 或 Registry。
  • ProcNodeActor: 這是透過 Akka Cluster Sharding 動態建立的 Actor,每一個 ProcNodeActor 負責一對一管理一個底層的作業系統進程 (OS Process)。它負責:
    • 啟動與停止 System.Diagnostics.Process
    • 監控 stdout / stderr 輸出。
    • 定期 Probe (探測) 子節點的 FSI 服務健康狀態(支援 Quartz Cron 或固定間隔)。
    • 若探測失敗超過閥值 (probeFailureThreshold) 或進程意外崩潰,會自動重啟進程。
    • 作為橋樑,將 FSI 相關的指令 (如 ForwardMessage) 轉發給子節點的 FSI Supervisor。
  • REST API (ProcRest): 提供 HTTP 介面供外部控制進程的啟動、停止與訊息發送。

2. ProcNode (進程節點模式)

被 Supervisor 啟動的子進程。通常是執行同樣的執行檔,但加上 --mode procnode 參數。

  • 啟動時會加入 Akka Cluster。
  • 啟動內建的 Akka.FSI.Supervisor (F# 互動環境監督者)。
  • 啟動成功後,會透過 Actor Selection 回傳 ProcNodeReady 訊息給 Supervisor,告知自身的 PID 與 FSI Actor 路徑。

REST API 介面

Supervisor 提供了一系列基於 Suave 的 HTTP API (預設 Port 為 6001)。

系統與叢集資訊

  • GET /healthGET /healthcheck: 系統健康檢查。
  • GET /api/cluster/info: 取得叢集狀態與角色。
  • POST /api/cluster/shutdown: 優雅地關閉所有子進程並關閉 Supervisor。

進程管理

  • GET /api/proc/nodes: 取得所有進程的狀態清單 (快照)。
  • POST /api/proc/nodes/start-default: 啟動一個預設的 ProcNode (執行自身並帶入 --mode procnode 等參數)。
    • Payload (Optional): { "procId": "自訂ID" }
  • POST /api/proc/nodes/start: 啟動一個自訂進程。
    • Payload: { "procId": "...", "fileName": "...", "args": [...], "workingDir": "...", "probeMessage": "...", "probeCron": "...", "probeIntervalMs": 15000 }
  • POST /api/proc/nodes/{procId}/stop: 停止指定的進程。
    • Payload (Optional): { "force": true }
  • POST /api/proc/nodes/clean-stopped: 清除 Registry 中已停止進程的紀錄。

FSI 互動 (透過 Probe 與 Send)

  • GET /api/proc/nodes/{procId}/probe: 取得進程目前的探測設定。
  • POST /api/proc/nodes/{procId}/probe: 更新探測設定。
    • Payload: { "probeMessage": "...", "probeCron": "...", "probeIntervalMs": 15000 }
  • GET /api/proc/nodes/{procId}/sessions: 取得該進程內 FSI Supervisor 的所有會話 (Sessions) 列表。
  • POST /api/proc/nodes/{procId}/send: 傳送指令到目標進程的 FSI Supervisor。
    • Payload: { "message": "執行字串", "timeoutMs": 30000 }

Message 格式 (傳送至 FSI)

透過 /api/proc/nodes/{procId}/send 傳送的 message 會由 ProcMessageParser 解析。支援的字串指令包含:

  • 執行 F# 程式碼: exec --session <sessionName> --code "<fsharp code>" [--refs ...] [--loads ...]
  • 取得 Session 資訊: getsession <sessionName>
  • 列出所有 Sessions: listsessions [--all true|false]
  • 建立 Checkpoint: checkpoint --session <sessionName> [--id <id>] [--comment <text>]
  • Fork Session: fork --fromsession <old> --newsession <new> [--checkpointid <id>]
  • Join Sessions: join --parentsession <parent> --childsessions <child1> <child2> [--reducer <code>]

啟動參數 (CLI Arguments)

Supervisor 模式啟動範例:

./Akka.Proc.Supervisor --mode supervisor --systemname "proc-system" --host 127.0.0.1 --port 5001 --spawndefault

這會啟動 Supervisor,並自動衍生一個預設的 ProcNode。

注意:

  1. 若你是直接執行 .dll,請使用 dotnet exec --runtimeconfig ... --depsfile ... Akka.Proc.Supervisor.dll ...,不要只寫 dotnet Akka.Proc.Supervisor.dll
  2. --spawndefault 依賴 bootstrap procnode;目前已驗證可用的最小 smoke 會顯式設定 --host/--port/--webhost/--webport
  3. POST /api/proc/nodes/{procId}/send 目前仍有一個已知限制,見文末「已知問題」。
  4. 若你在 deployed 環境排查 fsi-supervisor,不要直接拿既有 bootstrap/stale proc 的 fsiSupervisorPath 下結論。較可靠的順序是:
    • 先 direct ask proc-supervisor GetVesion
    • GetAllProcInfo
    • 必要時先 stop stale proc
    • 再顯式 StartProc 起一個 fresh procnode
    • 最後使用 fresh fsiSupervisorPath 做 direct ask / direct execution 驗證
  5. GetVesionfsi-supervisor timeout,不等於 fsi-supervisor 整體不可用;應至少再交叉驗證 ListSessions 或直接 ExecCode

--mode 與自訂 supervisee

--mode 只對 Akka.Proc.Supervisor.dll 自己 有意義:

  • --mode supervisor
    • 啟動 Supervisor process
  • --mode procnode
    • 啟動 ProcNode process,並在該 process 內 bootstrap Akka.FSI.Supervisor

若你透過 POST /api/proc/nodes/start 啟動的是別的程式,--mode 通常不該出現在該程式的 args 裡。

啟動另一個 .NET dll

如果 supervisee 是另一個 framework-dependent .NET dll,建議用:

{
  "procId": "my-dotnet-app",
  "fileName": "dotnet",
  "args": [
    "exec",
    "--runtimeconfig", "/path/MyApp.runtimeconfig.json",
    "--depsfile", "/path/MyApp.deps.json",
    "/path/MyApp.dll",
    "--arg1", "value1"
  ]
}

除非你啟動的仍然是 Akka.Proc.Supervisor.dll 本身,否則不要再加 --mode

啟動 Python

如果 supervisee 是 Python:

{
  "procId": "my-python-app",
  "fileName": "python3",
  "args": ["/path/app.py", "--arg1", "value1"]
}

同樣不需要 --mode

什麼情況下才要 --mode procnode

只有當你要啟動的 child process 本身就是 Akka.Proc.Supervisor.dll,而且要把它當成可回報 fsiSupervisorPath 的 FSI host 時,才需要 --mode procnode


probeMessage / probeIntervalMs 的用途與限制

  • probeMessage
    • 要定期送給 child proc 的 probe 內容
  • probeIntervalMs
    • 固定週期 probe 的毫秒數
  • probeCron
    • 若使用 Quartz cron,則用這個欄位排程 probe

這套 probe 機制不是 generic health check,也不是 generic IPC。實際流程是:

  1. ProcSupervisor 定時觸發 probe
  2. 讀出 probeMessage
  3. ProcMessageParser 解析該字串
  4. 轉送到 child proc 內的 fsi-supervisor
  5. 依回應是否成功來判定 probe success / failure

所以:

  • supervisee 若是 procnode + Akka.FSI.Supervisor
    • probeMessage 有意義
  • supervisee 若是一般 .NET dll、Python、或其他外部程式
    • 不應使用 FSI probe
    • /send 也不成立

推薦 probe 設定:FSI host / procnode

建議使用不改動 session state、且能穩定反映 FSI 可用性的指令:

{
  "probeMessage": "listsessions --all true",
  "probeIntervalMs": 15000
}

這是目前最建議的預設 probe。

一般自訂 .NET dll supervisee

若 child process 不是 procnode,建議不要設 probeMessage

{
  "probeMessage": null,
  "probeCron": null,
  "probeIntervalMs": null
}

Python supervisee

同樣不建議設 probeMessage

{
  "probeMessage": null,
  "probeCron": null,
  "probeIntervalMs": null
}

若要監控這類 process,應另外定義 generic health contract,而不是重用 FSI probe。


StartProc ask timeout 與 GetProcInfo 補收斂

在上層 orchestration(例如 fsharp-devkit create_fsi_host)中,StartProc 有時會出現:

  • child proc 其實已成功啟動
  • ProcRegistry 也已看得到 snapshot
  • StartProc 這個 ask-reply 還沒在 timeout 前回來

因此:

  • StartProc ask timeout
    • 不一定等於 host 建立失敗

較穩定的做法是:

  1. 先送 StartProc
  2. StartProc ask timeout
  3. 立刻在短時間內輪詢 GetProcInfo(procId)
  4. 若很快查到 snapshot,將其視為「已成功建立,但命令回覆較慢」
  5. 只有在短時間輪詢後仍查不到 snapshot,才真正當作建立失敗

這就是「StartProc ask timeout 時,改用 GetProcInfo 短時間輪詢補收斂」的意思。


E2E 範例 (FSX 腳本)

以下是一個已驗證可跑的 smoke 範例,示範如何透過 REST API 啟動一個 ProcNode、查詢節點、送出 F# 程式碼,最後再停止該 Node。

執行前請確認已啟動 Supervisor。若是 repo 內 Debug build,可用:

dotnet exec \
  --runtimeconfig Libs/Akka.Proc.Supervisor/bin/Debug/net10.0/Akka.Proc.Supervisor.runtimeconfig.json \
  --depsfile Libs/Akka.Proc.Supervisor/bin/Debug/net10.0/Akka.Proc.Supervisor.deps.json \
  Libs/Akka.Proc.Supervisor/bin/Debug/net10.0/Akka.Proc.Supervisor.dll \
  --mode supervisor \
  --systemname proc-system \
  --host 127.0.0.1 \
  --port 5001 \
  --webhost 127.0.0.1 \
  --webport 6001 \
  --spawnnone
#r "nuget: FSharp.Data"

open System
open System.Threading
open FSharp.Data

// 設定 Supervisor 的 API 網址
let baseUrl = "http://localhost:6001/api/proc/nodes"

printfn "=== 1. 啟動預設的 ProcNode ==="
let startResp = Http.RequestString(
    baseUrl + "/start-default",
    httpMethod = "POST",
    headers = [ HttpRequestHeaders.ContentType "application/json" ],
    body = TextRequest """{"procId": "demo-node-01"}"""
)
printfn "啟動回應: %s" startResp

// 等待一下讓 Node 啟動並連上 Cluster
printfn "等待 Node 準備就緒..."
Thread.Sleep(3000)

printfn "=== 2. 查詢 Node 列表 ==="
let nodesResp = Http.RequestString(baseUrl, httpMethod = "GET")
printfn "Nodes: %s" nodesResp

printfn "=== 3. 查詢該 Node 的 sessions ==="
let sessionsResp =
    Http.RequestString(
        sprintf "%s/demo-node-01/sessions" baseUrl,
        httpMethod = "GET"
    )
printfn "Sessions: %s" sessionsResp

printfn "=== 4. 傳送 F# 程式碼到該 Node 執行 ==="
let fsiCommand = """exec --session mysession --code "let add a b = a + b\nadd 5 7" """
let sendPayload = sprintf """{"message": "%s"}""" fsiCommand

let sendResp =
    Http.RequestString(
        sprintf "%s/demo-node-01/send" baseUrl,
        httpMethod = "POST",
        headers = [ HttpRequestHeaders.ContentType "application/json" ],
        body = TextRequest sendPayload
    )
printfn "執行結果: %s" sendResp

printfn "=== 5. 關閉該 Node ==="
let stopResp = Http.RequestString(
    sprintf "%s/demo-node-01/stop" baseUrl,
    httpMethod = "POST",
    headers = [ HttpRequestHeaders.ContentType "application/json" ],
    body = TextRequest """{"force": true}"""
)
printfn "停止回應: %s" stopResp

printfn "=== 完成 ==="

send 指令的轉義規則

/send 目前是以 CLI-like 字串協定配合 ProcMessageParser 解析,因此若你要在 --code 內放多行 F#,請用轉義字元:

  • \\n 代表換行
  • \\r 代表 CR
  • \\t 代表 tab
  • \\\" 代表雙引號
  • \\\\ 代表反斜線

例如:

exec --session mysession --code "let add a b = a + b\nadd 5 7"

會在送進 FSI 前被還原成真正的兩行 F# 程式碼。

失敗案例現在的預期行為

若 F# 程式本身有語法錯誤或編譯錯誤,/send 現在的預期回應是:

  • HTTP 仍正常回應
  • ExecResult.ok = false
  • diagnostics 內有 FCS 診斷
  • error.detail.errorType = "FSharp.Compiler.Interactive.Shell+FsiCompilationException"

也就是說,失敗會被表達成正常的 FSI 執行結果,而不是 transport/REST 反序列化錯誤。

關於設定檔 (.hocon)

系統會嘗試讀取目錄下的 .hocon 檔案,若不存在則使用內建預設值。您可以透過 akka.proc 區塊來調整 probe 週期、sharding 設定等:

akka.proc {
  system-name = "proc-system"
  probe-interval-ms = 15000
  probe-failure-threshold = 3
  restart-delay-ms = 3000
  web {
    host = "0.0.0.0"
    port = 6001
  }
}

部署診斷建議

若 deployed 環境看起來像:

  • proc-supervisor 可回 GetVesion
  • GetAllProcInfo 也有資料
  • fsiSupervisorPath 存在
  • fsi-supervisor GetVesion timeout

不要直接推論成 fsi-supervisor 壞掉。先做下面這組最小驗證:

  1. 用 direct actor ask 驗 proc-supervisor GetVesion
  2. 停掉 stale proc
  3. StartProc 起一個 fresh procnode
  4. 直接對 fresh fsiSupervisorPath
    • ListSessions
    • ExecCode

fsharp-devkit 的實際 deployment 驗證中,remote host isolationsession isolation 最終都是透過這種 direct actor-level 驗證確認成立;先前使用 bootstrap/stale proc target 的 timeout 不能直接當成底層 runtime 壞掉的證據。

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on FAkka.Proc.Supervisor:

Package Downloads
PulseTrade.Shared.fs

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.564.101.201 0 4/7/2026
1.562.101.201-dgx.25 43 3/30/2026
1.562.101.201-dgx.24 34 3/30/2026
1.562.101.201-dgx.23 41 3/30/2026
1.562.101.201-dgx.22 45 3/30/2026
1.562.101.201-dgx.21 45 3/30/2026
1.562.101.201-dgx.20 40 3/30/2026
1.562.101.201-dgx.19 40 3/29/2026
1.562.101.201-dgx.18 139 3/28/2026
1.562.101.201-dgx.17 131 3/28/2026
1.562.101.201-dgx.16 131 3/28/2026
1.562.101.201-dgx.15 135 3/28/2026
1.562.101.201-dgx.14 133 3/28/2026
1.562.101.201-dgx.13 132 3/28/2026
1.562.101.201-dgx.12 46 3/27/2026
1.562.101.201-dgx.11 40 3/26/2026
1.562.101.201-dgx.10 40 3/26/2026
1.562.101.201-dgx.9 40 3/26/2026
1.562.101.201-dgx.8 40 3/25/2026
1.562.101.201-dgx.7 43 3/25/2026
Loading failed