Dapr DotNet5 HTTP 呼叫
版本介紹
- Dotnet 版本:5.0.100
- Dapr dotnet 版本:0.12.0-preview01
注意: Asp.Net Core
專案中的 launchSettings.json 檔案,該檔案的中的埠號應和 darp --app-port 埠號相同,否則 dapr 無法正常啟動 Asp.Net Core
專案。
工程結構
3 個 .NET 5 專案,ClientA、ServiceB、ServiceC。1 個 .NET Standard 專案,Dtos 。Dtos 用於儲存各種傳輸模型。呼叫路徑如下圖所示。新建兩個 service 的意義在於展示 http 鏈路呼叫通過 dapr 如何實現。
- dotnet5-client-a 做為客戶端呼叫服務 dotnet5-service-b;
- dotnet5-service-b 做為服務中轉,既收來自 dotnet5-client-a 客戶端的請求,又發起對 dotnet5-service-c 的呼叫;
- dotnet5-service-c 響應 dotnet5-service-b 的請求;
- dotnet5-service-b 響應 dotnet5-client-a 的請求。
ServiceC
ServiceC 做為 http 呼叫鏈路呼叫終端只需監聽 http 呼叫埠。通過 nuget 包管理工具,選中->Show pre-release packages,搜尋 dapr ,選中 Dapr.AspNetCore 安裝包。
Startup
在 ConfigureServices(IServiceCollection services) 方法中通過鏈式呼叫 AddDapr() 方法註冊 Dapr 到 IOC 容器中。內容如下:
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ServiceC
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddDapr().AddJsonOptions(options => {
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
}
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
HelloController
在 HelloController 中新增 Talk 方法,列印接收的資訊並告訴呼叫方當前服務是誰。具體內容如下:
[ApiController]
public class HelloController : Controller
{
[HttpPost("talk")]
public async Task<SomeResponseBody> Talk(SomeRequestBody someRequestBody)
{
Console.WriteLine(string.Format("{0}:{1}", someRequestBody.Id, someRequestBody.Time));
return await Task.FromResult(new SomeResponseBody
{
Msg = "This is ServiceC"
});
}
}
launchSetting.json
profiles.ServiceC.applicationUrl 埠號一定要修改為 --app-port 相同的埠號,否則通過 dapr 啟動專案的時候無法正常啟動
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:35737",
"sslPort": 44379
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"ServiceC": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:9201",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
啟動
dapr run --app-id dotnet-server-c --app-port 9201 --dapr-http-port 3520 dotnet run
ServiceB
ServiceB 做為呼叫鏈中的一箇中轉節點,既要監聽服務,同時還要發起請求。由於 Dapr.AspNetCore
已經引用了 Dapr.Client
。因此不需要再次引用 Dapr.Client
。
Startup
下面是 Dapr.AspNetCore AddDapr() 原始碼,從原始碼中可知 AddDapr() 方法向控制器中註冊 Dapr 整合。同時通過依賴注入容器註冊 DaprClient 。DaprClient 可以和 Dapr 執行時互動。比如 HTTP 呼叫,也正因為如此,ServiceB 的 Startup 檔案我們只需拷貝 ServiceC 的 Startup 檔案即可。原始碼如下:
/// <summary>
/// Provides extension methods for <see cref="IMvcBuilder" />.
/// </summary>
public static class DaprMvcBuilderExtensions
{
/// <summary>
/// Adds Dapr integration for MVC to the provided <see cref="IMvcBuilder" />.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder" />.</param>
/// <param name="configureClient">The (optional) <see cref="DaprClientBuilder" /> to use for configuring the DaprClient.</param>
/// <returns>The <see cref="IMvcBuilder" /> builder.</returns>
public static IMvcBuilder AddDapr(this IMvcBuilder builder, Action<DaprClientBuilder> configureClient = null)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
// This pattern prevents registering services multiple times in the case AddDapr is called
// by non-user-code.
if (builder.Services.Any(s => s.ImplementationType == typeof(DaprMvcMarkerService)))
{
return builder;
}
builder.Services.AddDaprClient(configureClient);
builder.Services.AddSingleton<DaprMvcMarkerService>();
builder.Services.AddSingleton<IApplicationModelProvider, StateEntryApplicationModelProvider>();
builder.Services.Configure<MvcOptions>(options =>
{
options.ModelBinderProviders.Insert(0, new StateEntryModelBinderProvider());
});
return builder;
}
private class DaprMvcMarkerService
{
}
}
HelloController
通過構造器注入 DaprClient 以發起 Http 呼叫 ServiceC 提供的服務。
[ApiController]
public class HelloController : ControllerBase
{
private readonly DaprClient daprClient;
public HelloController(DaprClient daprClient)
{
this.daprClient = daprClient;
}
[HttpPost("talk")]
public async Task<SomeResponseBody> Talk(SomeRequestBody someRequestBody)
{
var data = new { Time = DateTime.Now.ToLongDateString(), Id = "This is Service C." };
HTTPExtension httpExtension = new HTTPExtension()
{
Verb = HTTPVerb.Post
};
SomeResponseBody responseBody = await daprClient.InvokeMethodAsync<object, SomeResponseBody>("dotnet-server-c", "talk", data, httpExtension);
Console.WriteLine(string.Format("{0}:{1} \n recieve message:{2}", someRequestBody.Id, someRequestBody.Time, responseBody.Msg));
return await Task.FromResult(new SomeResponseBody
{
Msg = "This is ServiceB"
});
}
launchSetting.json
參考 ServiceC 更改埠號。
啟動
dapr run --app-id dotnet-server-b --app-port 9200 --dapr-http-port 3521 dotnet run
ClientA
ClientA 的目的是發起對 ServiceB 服務的呼叫,因此只需新增 Dapr.Client
用於和 Dapr 執行時互動即可。內容如下:
class Program
{
static async Task Main(string[] args)
{
var jsonOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
};
var client = new DaprClientBuilder()
.UseJsonSerializationOptions(jsonOptions)
.Build();
var data = new { Time = DateTime.Now.ToLongDateString(), Id="This is Client A" };
HTTPExtension httpExtension = new HTTPExtension()
{
Verb = HTTPVerb.Post
};
while (true)
{
var a = await client.InvokeMethodAsync<object, SomeResponseBody>("dotnet-server-b", "talk", data, httpExtension);
Console.WriteLine(a.Msg);
await Task.Delay(5 * 1000);
}
}
}
每間隔 5 秒向 ServiceB 傳送一次請求。
啟動
dapr run --app-id dotnet5-http-client dotnet run
ClientA 接收內容:
== APP == This is ServiceB
SerivceB 接收內容:
== APP == This is Client A:2020年11月27日 星期五
== APP == recieve message:This is ServiceC
ServiceC 接收內容:
== APP == This is Service C.:2020年11月27日 星期五
總結
至此,DOTNET5 通過 dapr HTTP 呼叫的示例就結束了。