Blazor+Dapr+K8s微服務之開發環境除錯

小莊發表於2021-08-24

 

1         安裝Dapr開發除錯環境

1.1         Dapr 完整安裝模式不支援開發除錯

在上一篇隨筆《Blazor+Dapr+K8s微服務之服務呼叫》中,我們通過為每個微服務執行dapr run ….dotnet run命令,以自宿主的方式在本地開發環境成功執行了服務呼叫的例子。

但是,這種執行方式是不支援除錯的,我們無法在程式中進行斷點。這就很不實用了!

搜尋一番,找到這麼一篇文章:Simple approach to run and debug multiple .NET Dapr projects (w/o Docker) - DEV Community,根據文章,我們可以不依賴Docker,就可以對我們的Dapr微服務本地進行除錯。

其主要原理是:通過Dapr簡易安裝的Daprd命令,以自宿主方式先啟動每個微服務的Dapr SideCar,  指定這些SideCar 監聽的每個微服務的Http埠,然後,我們就可以在VS中按照監聽埠啟動每個微服務,這樣我們的微服務就能夠和Dapr的SideCar實現通訊,從而實現服務呼叫,狀態儲存的功能。

1.2         修改Dapr完整安裝模式為簡易模式

我們需要先解除安裝我們已經安裝好的完整模式,命令如下:

Dapr uninstall

然後安裝簡易模式,命令如下:

dapr init --slim

 

2         通用Dapr開發除錯指令碼

我對文章中作者的指令碼做了簡化處理,實現了一個”通用Dapr 微服務開發除錯資料夾”,以方便我們可以利用這個資料夾對任何Dapr微服務進行除錯。該資料夾結構如下:

 

2.1         微服務Dapr SideCar啟動指令碼。

其中start-daprd.ps1為啟動每個微服務SideCar的PowerShell指令碼,內容如下:

# pre-requisites:
# - initialized with: dapr init --slim
#   so that redis is running as container and placement service is started on demand

# --------------------------------------------------------------------------------
# projects
# - appId       = needs to be Dapr id commonly used to address service

$configProjects = @(
    @{
        appId       = "blazorweb"
    }
    @{
        appId       = "serviceapi1"
    }
)

# --------------------------------------------------------------------------------
# INIT

$ErrorActionPreference = "Stop"

# stop and remove previous jobs
$jobNamePattern = $configProjects | Join-String -Property appId -Separator "|" -OutputPrefix "(placement|" -OutputSuffix ")"
Get-Job | ? { $_.Name -match $jobNamePattern } | Stop-Job -PassThru | Remove-Job

# --------------------------------------------------------------------------------
# MAIN

$jobs = @()

# start placement service/job
$DAPR_PLACEMENT_PORT = 6050
$jobName = "placement"
Start-Job -Name $jobName -ScriptBlock {
    param( $port )
    
    placement --port $port

} -Argument $DAPR_PLACEMENT_PORT

Write-Host "started" $jobName "in background, listening port:"$DAPR_PLACEMENT_PORT
"-" * 80
$jobs += $jobName

# start jobs for app and dapr sidecar
$DAPR_HTTP_PORT = 3500
$DAPR_GRPC_PORT = 50001
$METRICS_PORT = 9091
$APP_PORT = 5000

foreach ($configProject in $configProjects) {

    $jobName = $configProject.appId + "-daprd"
    $componentsPath = "components/"
    $configFile = "config.yaml"

    Start-Job -Name $jobName -ScriptBlock {
        param( $appId, $appPort, $DAPR_HTTP_PORT, $DAPR_GRPC_PORT, $DAPR_PLACEMENT_PORT, $METRICS_PORT, $componentsPath, $configFile)

        daprd --app-id $appId  `
            --app-port $appPort `
            --placement-host-address $("localhost:" + $DAPR_PLACEMENT_PORT) `
            --log-level debug `
            --components-path $componentsPath `
            --config $configFile `
            --dapr-http-port $DAPR_HTTP_PORT `
            --dapr-grpc-port $DAPR_GRPC_PORT `
            --metrics-port $METRICS_PORT

    } -Argument $configProject.appId, $APP_PORT, $DAPR_HTTP_PORT, $DAPR_GRPC_PORT, $DAPR_PLACEMENT_PORT, $METRICS_PORT, $componentsPath, $configFile
    Write-Host "started "$jobName" in background, DAPR_HTTP_PORT: "$DAPR_HTTP_PORT "DAPR_GRPC_PORT:"$DAPR_GRPC_PORT "METRICS_PORT:"$METRICS_PORT
    Write-Host "expecting "$configProject.appId" to be started on listening port:"$APP_PORT 
    "-" * 80
    $jobs += $jobName

    $DAPR_HTTP_PORT += 10
    $DAPR_GRPC_PORT += 10
    $APP_PORT += 10
    $METRICS_PORT += 1
}


# handle menu

$running = $true

while ($running) {
    Write-Host "s: job status"
    Write-Host "e: check all logs for errors"
    Write-Host "q: stop jobs and quit"
    $jobId = 0
    foreach ($job in $jobs) {
        Write-Host $($jobId.ToString() + ": show log of " + $job)
        $jobId += 1
    }

    $option = Read-Host "Enter option"

    switch ($option.ToUpper()) {
        "S" {
            Get-Job | ? { $_.Name -match $jobNamePattern } | Format-Table Name, State
        }
        "E" {
            foreach ($job in $jobs) {
                $errors = $null
                if ($job -match "-app$") {
                    $errors = (Receive-Job -Name $job -Keep) -match "(error|fail)\:"
                }
                else {
                    $errors = (Receive-Job -Name $job -Keep) -match "level\=error"
                }
                if ($errors) {
                    "-" * 80
                    Write-Host "ERROR IN JOB:" $job -ForegroundColor Red
                    $errors
                }
            }
        }
        "Q" {
            Get-Job | ? { $_.Name -match $jobNamePattern } | Stop-Job -PassThru | Remove-Job
            $running = $false            
        }
        default {
            if ([int32]::TryParse($option , [ref]$jobId )) {
                if ($jobId -ge 0 -and $jobId -lt $jobs.Count) {
                    Receive-Job -Name $jobs[$jobId] -Keep | code -
                }
            }
        }
    }
}

 

對於每個微服務,我們需要在變數configProjects中定義每個微服務的名字/ID,這裡使用的是上一節服務呼叫中的兩個微服務的名字 blazorweb和serviceapi1。其它內容不需要變動。

2.2         其它配置檔案

其中components資料夾和config.yml 分別定義了SideCar啟動需要的元件和配置。元件包括訂閱釋出元件和狀態儲存元件,如下:

 

這兩個元件的內容很簡單,都是連結到一個Redis服務,提供訂閱釋出需要的訊息佇列和狀態儲存需要的快取。這兩個元件的檔案和config.yml配置檔案其實我是拷貝的完整安裝目錄(C:\Users\XXX\.dapr)下面的檔案。

 

3         除錯服務呼叫

3.1         為每個微服務啟動Dapr SideCar 服務

啟動每個微服務的SideCar:

 

可以看到,我們以Job方式分別啟動了三個服務,分別是,placement, blazorweb-daprd 和serviceapi1-daprd,  placement 服務是Dapr用來實現Actor模式的。而blazorweb-daprd 和serviceapi1-daprd則分別是我們兩個微服務的Dapr SideCar。兩個SideCar分別要求我們的微服務在5000和5010埠啟動。

需要說明的是,啟動微服務SideCar之前,先要啟動Dapr元件需要的Redis服務,我這邊啟動了一個簡易的Windows Redis Server。

 

這時候,我們檢視每個服務的狀態都是執行狀態:

 

3.2         啟動每個微服務

我們在VS中,修改每個微服務宿主專案的launchSettings檔案,分別在5000埠和5010埠啟動我們的兩個微服務:

 

 

3.3         除錯服務呼叫

訪問http://localhost:5000,

 

我們在DaprTest1.ServiceApi1專案的WeatherForecastController.cs檔案中打個斷點,然後點選Blazor介面中的Fetch Data選單,可以看到程式停在了我們的斷點上。

 

這裡需要說明一個問題,我們在做服務呼叫的時候,在簡易安裝模式下,Dapr會始終呼叫127.0.0.1的3500埠,例如http://127.0.0.1:3500/v1.0/invoke/serviceapi1/method/WeatherForecast,並不會呼叫當前微服務SideCar指定的Dapr Http 埠。其實,我們如果訪問http://127.0.0.1:3510/v1.0/invoke/serviceapi1/method/WeatherForecast,也是可以呼叫微服務的。

 相關程式碼:iamxiaozhuang/dapr-test (github.com)

 

相關文章