7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

.NET 6 と Daprを使った分散サービス開発 その3 Service Invocationによるマイクロサービス間でのコールとステップ実行

Last updated at Posted at 2022-03-01

Service Invocation(サービス間起動)によるマイクロサービス間でのコール

今回は、Daprの機能の一つであるサービス間呼び出しを行います。前回の記事は以下からどうぞ

おさらいします、今回やりたいのは、開発者がローカル環境でAppをコールした際に、バックエンドのServiceAとServiceBをコールして、レスポンスが返ってくる事です。

image.png

フロントREST APIを担当する Appのプロジェクト から バックエンドREST APIであるService A と同じく バックエンドREST APIであるService Bを呼び出し、その結果をマージして返却するような仕組みを作ります。

  1. 開発者はローカルからAppのREST APIへGETをコール(1)
  2. Service A バックエンドにService Invocation(サービス間起動)でコール(2)
  3. Service B バックエンドにService Invocation(サービス間起動)でコール(3)
  4. Appは結果をマージしてレスポンスを返却

各プロジェクトの編集

Service A プロジェクト

ほぼ、プロジェクトのデフォルト状態サンプルであるWeatherForecastのそのままです。以下の一か所だけコメントします。
//app.UseHttpsRedirection(); フロントのAppはhttpsでトリガされますが、サービス間コールではhttpを使うようにします。このコメントを行った後は、このサービスにtyeで割り当てられたhttpsポートでのアクセスに意味が無くなる事も把握しておいてください。

Program.cs
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ポートでのアクセスに意味が無くなる事も把握しておいてください。

Program.cs
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ですね。

image.png

.NET向けには二つのライブラリが提供されており。

  • Dapr.AspNetCore (ASP .NET Core向け)
  • Dapr.Client (コンソールプログラムや他一般向け)

Program.csの編集

Appプロジェクトの Program.cs を編集します。
builder.Services.AddDaprClient(); の箇所で、DIコンテナにサービスとしてDapr Clientを登録しています。

Program.cs
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を使ってサービス間起動を行っています。

WeatherForecastController.cs
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として、複数プロセスを起動した事を想像してみてください。

image.png

サービス名ってどこで設定した? Service Invocation(サービス間起動)

**あれ?名前ってどこにあったっけ?**って思いますよね、tye.yamlを改めて見てみましょう。

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ってのがサービス名です。

tye run のログより
[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で起動してみましょう。

image.png

起動したら、ダッシュボードからAppのURLを確認して...

image.png

今回はポート65094が割り当てられていました、https://localhost:65094/swagger/index.html って感じでアクセスしてみます。見ての通り、ServiceAの値5つ、ServiceBの値5つを合わせた10個の値が返却されてきます。

image.pngimage.png

デバッグとステップ実行を試す(VS 2022)

Visual Studio 2022を使ったステップ実行を試してみます。マイクロサービスでのデバッグの面倒さは、各サービスが複雑に絡み合ってサービス連携している点だと思います。

今回でいえば、以下の図のように、Appからリクエストをトリガして、ServiceAで設定したブレイクポイントで止まってほしいわけです。

image.png

プロセスをアタッチ

まず、tyeで起動したプロセスにアタッチします。.NETの場合デフォルトの場合はプロジェクト名.exeみたいに起動していると思います。

image.png

image.png

ブレイクポイントを張る

Visual Studio 2022からソリューションを開きます。ServiceAプロジェクトの WeatherForecastController.cs にブレイクポイントを張ります。

image.png

トリガする

あとは、App側のSwaggerからリクエストを送れば、こんな感じでブレイクします。

image.png
image.png

デバッグとステップ実行を試す(VS Code)

VS Codeは、拡張機能として、Dapr拡張とTye拡張が提供されています。まずは、インストールしておきます。

image.png

image.png

VS Codeでフォルダとしてプロジェクトを開き、プロジェクトにタスクを追加

F1キー(もしくは、ctrl+shift+p)、Tye: Scaffold Tye Taksでタスクを生成します。.vscodeフォルダにはtasks.jsonとlaunch.jsonが生成されます。ついでに、workspaceとして保存もしておきましょう。

image.png

tyeの実行

F1キー(もしくは、ctrl+shift+p)、Task:runでタスクを実行しましょう。

image.png

タスクに定義されたtye-runが表示されているので、実行しましょう。

image.png

実行すると、以下のように統合ターミナルでtyeが実行されているのがわかります。停止する場合はターミナルからctrl+cで停止します。

image.png

tye拡張による各サービスの確認

左のTyeのペインから起動している各サービスを確認する事ができます。ダッシュボード機能と同じよう感じです。

image.png

ブラウザのマークをクリックすると、バインドされているポートでブラウザが起動し、デバッグマークでプロセスをアタッチしてくれます。
image.png

ステップ実行

ここでは、Appプロジェクトの WeatherForecastController.cs のGETメソットコール時の所でブレイクポイントを設定しました。

image.png

F5キーでデバッグを開始します。watchモード というデバッグモードで起動します。

image.png

tyeエクスプローラーからブラウザを起動しましょう。同じようにswaggerからリクエストを送ってみます。

image.png

このエクスプローラーからは、各サービス個別のログも確認できます。(一番右のメモ帳みたいなマーク)

image.png

上部のデバッグ用のペインからはアタッチするサービスプロセスを変更する事ができます。

image.png

Swaggerからリクエストを送り、ブレイクさせます。

image.png

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?