地端測試所需軟體在Windows/Linux/macOS都有支援:
在Windows環境建議先安裝scoop這個指令列套件管理工具,假如你的系統槽(C:)太小空間已剩不多的話,可以設定Scoop的安裝路徑到其他磁碟的空間然後再安裝scoop。
再來使用 scoop install nvm
指令安裝nvm for windows,然後用 nvm install lts; nvm use lts
安裝並指定命令列採用LTS版本的nodejs & npm版本,然後繼續用npm方式安裝Azurite指令列工具:
npm install -g azurite
mkcert也可用scoop安裝:
scoop bucket add extras
scoop install mkcert
這種安裝方式的好處是不需要系統Admin權限就可使用,且之後不需要這些指令列工具時,直接反安裝scoop即可。
Azure Storage Explorer在Windows環境可直接用 winget install Microsoft.AzureStorageExplorer
這個 winget 指令安裝:
建立一個空目錄來放置Azure Storage Emulator的資料,例如 d:\azurite
,然後在這個目錄下執行以下指令來啟動純http的Azure Storage Emulator:
azurite --location data --debug debug.log
當出現如下圖所示的訊息時,表示Azure Storage Emulator已啟動成功,並且在azurite目錄底下會產生 data 目錄用來存放模擬服務的資料,需要有訊息記錄log時也可從 debug.log 紀錄檔查找:
要結束服務,按Ctrl+C即可
我們用一個新的 ASP.NET Core WebApi專案來示範如何使用Azure Table Storage Provider:
dotnet new webapi --framework net6.0 --name RpcDemo.Hosting.AspNetCoreWebApi
並將此專案加入至根目錄的OrleansDemo.sln方案中Program.cs
檔案,為以下內容:
using Orleans.Hosting;
var builder = WebApplication.CreateBuilder(args);
// Add Orleans co-hosting
builder.Host.UseOrleans(siloBuilder =>
{
siloBuilder.UseLocalhostClustering();
siloBuilder.AddAzureTableGrainStorage(
name: "demo_counters", options =>
{
options.UseJson = true;
options.ConfigureTableServiceClient("UseDevelopmentStorage=true");
});
});
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
修改的部分為新增了Orleans的 ASP.NET Core co-hosting(使用擴充方法 UseOrleans()
)配置程式碼:AddAzureTableGrainStorage()
擴充方法是位於 Microsoft.Orleans.Persistence.AzureStorage Nuget套件之中,第一個 name
參數要跟 RpcDemo.Grains.Counters 專案內 CounterGrain
建構子宣告的Grain State所裝飾的PersistentStateAttribute
屬性語法第二個參數的值一致:using Microsoft.AspNetCore.Mvc;
using Orleans;
using RpcDemo.Interfaces.Counter;
namespace RpcDemo.Hosting.AspNetCoreWebApi.Controllers;
[ApiController]
[Route("[controller]")]
public class CounterController
{
private readonly IGrainFactory _grainFactory;
public CounterController(IGrainFactory grainFactory)
{
_grainFactory = grainFactory;
}
[HttpGet]
public async Task<int> GetCurrent(Guid id)
{
var counter = _grainFactory.GetGrain<ICounterGrain>(id);
return await counter.GetCountAsync();
}
[HttpPost]
public async Task Increment(Guid id)
{
var counter = _grainFactory.GetGrain<ICounterGrain>(id);
await counter.IncrementAsync();
}
[HttpPatch]
public async Task Reset(Guid id)
{
var counter = _grainFactory.GetGrain<ICounterGrain>(id);
await counter.ResetAsync();
}
}
這個CounterController的程式碼裡,藉由注入Orleans的 IGrainFactory
物件,配合Action方法輸入的參數來取得 ICounterGrain
的Grain實體,並呼叫其RPC方法來操作記數器(也就是Grain State的資料)。{
"label": "build counter webapi",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/Hosting/Server/RpcDemo.Hosting.AspNetCoreWebApi/ RpcDemo.Hosting.AspNetCoreWebApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
{
"name": "Launch WebApi",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build webapi",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/Hosting/Server/RpcDemo.Hosting.AspNetCoreWebApi/bin/Debug/net6.0/RpcDemo.Hosting.AspNetCoreWebApi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+https://\\S+:([0-9]+)",
"uriFormat": "https://localhost:%s/swagger/index.html"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"console": "integratedTerminal"
}
然後開一個作業系統的命令列視窗啟動 Azurite,然後在Visual Studio Code的 Run And Debug 功能,選取 "Launch WebApi",讓WebApi專案跑起來,跳出的Swagger網頁可以進行手動呼叫WebApi進行測試:
(可使用Online GUID Generator來產生測試用GUID字串值)
呼叫幾次使記數器增加或重設的WebApi之後,用Azure Storage Explorer來查看記數器的Grain State資料:
經過一些實際WebApi呼叫的操作後,可以發現有兩個注意點:
AzureTableStorageOptions
的 UseJson = true
,所以才會序列化為JSON字串以便於觀察);所以,雖然理論上Orleans框架沒有限制Grain State的定義狀態資料的最大空間限制,但實際上還是會被其Silo的Persistence Storage Provider使用的儲存資料庫機制所限制。基本上跟使用Azure Table Storage Provider的步驟一樣,只需要把原本呼叫 AddAzureTableGrainStorage()
的Azure Table Storage Provider配置程式碼改成呼叫 AddAzureBlobGrainStorage()
指定用Azure Blob Storage Provider即可:
siloBuilder.AddAzureBlobGrainStorage(
name: "demo_counters", options =>
{
options.UseJson = false;
options.ConfigureBlobServiceClient("UseDevelopmentStorage=true");
});
這邊使用 UseJson = false
,然後跑起來之後嘗試幾個WebApi呼叫讓CounterGrain存資料至Azure Blob之後,用Azure Storage Explorer觀察:
可以看到預設的Azure Blob Storage Provider的行為是序列化成二進位資料之後,再存為Blob檔案內容,可以有效縮小儲存Grain State所需空間。
整個完成的範例程式GitHub專案在:https://github.com/windperson/OrleansRpcDemo/tree/day11
明天會繼續介紹當正式環境的雲端Azure Table/Blob Storage使用"受控識別(Managed Identity)"控制存取權限保護資源時,Orleans 的Silo要如何設定Table/Blob Storage Provider。