Service Invocation(サービス間起動)によるマイクロサービス間でのコール
今回は、Daprの機能の一つであるサービス間呼び出しを行います。前回の記事は以下からどうぞ
おさらいします、今回やりたいのは、開発者がローカル環境でAppをコールした際に、バックエンドのServiceAとServiceBをコールして、レスポンスが返ってくる事です。
フロントREST APIを担当する Appのプロジェクト から バックエンドREST APIであるService A と同じく バックエンドREST APIであるService Bを呼び出し、その結果をマージして返却するような仕組みを作ります。
- 開発者はローカルからAppのREST APIへGETをコール(1)
- Service A バックエンドにService Invocation(サービス間起動)でコール(2)
- Service B バックエンドにService Invocation(サービス間起動)でコール(3)
- Appは結果をマージしてレスポンスを返却
各プロジェクトの編集
Service A プロジェクト
ほぼ、プロジェクトのデフォルト状態サンプルであるWeatherForecastのそのままです。以下の一か所だけコメントします。
//app.UseHttpsRedirection(); フロントのAppはhttpsでトリガされますが、サービス間コールではhttpを使うようにします。このコメントを行った後は、このサービスにtyeで割り当てられたhttpsポートでのアクセスに意味が無くなる事も把握しておいてください。
var builder = WebApplication.CreateBuilder(args);
// 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();
Service B プロジェクト
Aと同様で、プロジェクトのデフォルト状態サンプルであるWeatherForecastのそのままです。以下の一か所だけコメントします。
//app.UseHttpsRedirection(); フロントのAppはhttpsでトリガされますが、サービス間コールではhttpを使うようにします。このコメントを行った後は、このサービスにtyeで割り当てられたhttpsポートでのアクセスに意味が無くなる事も把握しておいてください。
var builder = WebApplication.CreateBuilder(args);
// 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();
App プロジェクト
Appプロジェクトでは、Dapr越しに、他のサービスをコールしますので、Daprのクライアントライブラリを導入します。もちろん、Daprではドキュメントを読んでもらうとわかりますが、自前でcurlでPOSTしたり、GRPCでコールしたりなどできますが、SDKで便利にさぼります。現在SDKは、.NET , Python, Java, Go, PHP, Javascript(Node.JS)がstable、C++とRustが開発中です。
Client SDK追加
コマンドラインからの場合、Appプロジェクトフォルダのルートに移動して、以下のコマンドを実行してください。
$ dotnet add package Dapr.AspNetCore
Visual Studio 2022では、以下の作業になります。パッケージを右クリックして、nugetですね。
.NET向けには二つのライブラリが提供されており。
- Dapr.AspNetCore (ASP .NET Core向け)
- Dapr.Client (コンソールプログラムや他一般向け)
Program.csの編集
Appプロジェクトの Program.cs を編集します。
builder.Services.AddDaprClient(); の箇所で、DIコンテナにサービスとしてDapr Clientを登録しています。
var builder = WebApplication.CreateBuilder(args);
// 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();
// Add Dapr Client << 以下を追加
builder.Services.AddDaprClient();
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();
WeatherForecastController.csの編集
WeatherForecastControllerで、Dapr clientを使い Service Invocation(サービス間起動)でServiceAとServiceBをそれぞれコールします。
それぞれのから得られた5件づつの結果をConcatして10件にして返しています。(すません、手抜きです。)
プロパティとして_daprClientを追加、コンストラクタでは、Program.csで登録したDIコンテナからdaprClientサービスを引っ張り出しています。
メソッド、GetWeatherForecastでは、この_daprClientを使ってサービス間起動を行っています。
using Dapr.Client;
using Microsoft.AspNetCore.Mvc;
namespace App.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
private readonly DaprClient _daprClient;
public WeatherForecastController(ILogger<WeatherForecastController> logger, DaprClient daprClient)
{
_logger = logger;
_daprClient = daprClient;
}
[HttpGet(Name = "GetWeatherForecast")]
public async Task<IEnumerable<WeatherForecast>> GetAsync()
{
IEnumerable<WeatherForecast>? forecastsA = await _daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, "service-a", "weatherforecast");
IEnumerable<WeatherForecast>? forecastsB = await _daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, "service-b", "weatherforecast");
IEnumerable<WeatherForecast>? forecastsC = forecastsA.Concat(forecastsB);
return forecastsC.ToArray();
}
}
Service Invocation(サービス間起動)
もう少し、詳しくサービスを見てみます。
サービス名とメソッド名のみが記載されており、どのポートで起動しているかなどは一切コードに記載がありません。Daprでは、mDNS を使って名前解決を行っているからです。ポートやIPを意識する事無く、お互いのサービスをコールする事ができます。
_daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, "service-a", "weatherforecast");
_daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, "service-b", "weatherforecast");
何それ何がうれしいの?って思われるかもしれません。Kubernetes などにPodとして、複数プロセスを起動した事を想像してみてください。
サービス名ってどこで設定した? Service Invocation(サービス間起動)
**あれ?名前ってどこにあったっけ?**って思いますよね、tye.yamlを改めて見てみましょう。
~~~~前略~~~~
services:
- name: service-a
project: ServiceA/ServiceA.csproj
- name: service-b
project: ServiceB/ServiceB.csproj
- name: app
project: App/App.csproj
ありましたよね?
tye runで実行した際のログには、Daprを起動しているログが残されていますが、ここの引数でサービス名を指定しています。ここの--app-id service-bってのがサービス名です。
[20:37:04 INF] Launching service service-b-dapr_1c945e4a-7: C:/dapr/dapr.exe run --app-id service-b --dapr-grpc-port 56373 --dapr-http-port 56374 --metrics-port 56375 --app-port 56366 --log-level debug
Service Invocation(サービス間起動)を試す
tyeで起動してみましょう。
起動したら、ダッシュボードからAppのURLを確認して...
今回はポート65094が割り当てられていました、https://localhost:65094/swagger/index.html って感じでアクセスしてみます。見ての通り、ServiceAの値5つ、ServiceBの値5つを合わせた10個の値が返却されてきます。
デバッグとステップ実行を試す(VS 2022)
Visual Studio 2022を使ったステップ実行を試してみます。マイクロサービスでのデバッグの面倒さは、各サービスが複雑に絡み合ってサービス連携している点だと思います。
今回でいえば、以下の図のように、Appからリクエストをトリガして、ServiceAで設定したブレイクポイントで止まってほしいわけです。
プロセスをアタッチ
まず、tyeで起動したプロセスにアタッチします。.NETの場合デフォルトの場合はプロジェクト名.exeみたいに起動していると思います。
ブレイクポイントを張る
Visual Studio 2022からソリューションを開きます。ServiceAプロジェクトの WeatherForecastController.cs にブレイクポイントを張ります。
トリガする
あとは、App側のSwaggerからリクエストを送れば、こんな感じでブレイクします。
デバッグとステップ実行を試す(VS Code)
VS Codeは、拡張機能として、Dapr拡張とTye拡張が提供されています。まずは、インストールしておきます。
VS Codeでフォルダとしてプロジェクトを開き、プロジェクトにタスクを追加
F1キー(もしくは、ctrl+shift+p)、Tye: Scaffold Tye Taksでタスクを生成します。.vscodeフォルダにはtasks.jsonとlaunch.jsonが生成されます。ついでに、workspaceとして保存もしておきましょう。
tyeの実行
F1キー(もしくは、ctrl+shift+p)、Task:runでタスクを実行しましょう。
タスクに定義されたtye-runが表示されているので、実行しましょう。
実行すると、以下のように統合ターミナルでtyeが実行されているのがわかります。停止する場合はターミナルからctrl+cで停止します。
tye拡張による各サービスの確認
左のTyeのペインから起動している各サービスを確認する事ができます。ダッシュボード機能と同じよう感じです。
ブラウザのマークをクリックすると、バインドされているポートでブラウザが起動し、デバッグマークでプロセスをアタッチしてくれます。
ステップ実行
ここでは、Appプロジェクトの WeatherForecastController.cs のGETメソットコール時の所でブレイクポイントを設定しました。
F5キーでデバッグを開始します。watchモード というデバッグモードで起動します。
tyeエクスプローラーからブラウザを起動しましょう。同じようにswaggerからリクエストを送ってみます。
このエクスプローラーからは、各サービス個別のログも確認できます。(一番右のメモ帳みたいなマーク)
上部のデバッグ用のペインからはアタッチするサービスプロセスを変更する事ができます。
Swaggerからリクエストを送り、ブレイクさせます。