.NET 6 と Daprを使った分散サービス開発 その6 Input Binding で Scheduler実行

# Input Binding (入力バインディング)による連携

今まで、以下の流れで作業を行ってきました。今回はDaprに備わっているInput Binding (入力バインディング)の中でも、最もわかりやすいと思うスケジューラーによる定期実行に触れていきたいと思います。

**今までの前提の上で入力バインディングの機能を追加しますので、このページから読まれている方は、前提として以下を読んできてください。**

----
https://qiita.com/kazumihirose/items/701fcbfb397fe8670bf7

https://qiita.com/kazumihirose/items/016dde8ca9b35d1030cc

https://qiita.com/kazumihirose/items/e996e3cc34c511bc1124

https://qiita.com/kazumihirose/items/f7811bf3e5abe405a551

https://qiita.com/kazumihirose/items/57e3aeaab65a1a68977b

## Input Bindingについて

Input Binding (入力バインディング)は、例えば5分毎(イベント)でサービスをコール（トリガ）してほしい、Azure Storage Queue / Amazon SQS / Apache Kafkaなど所謂キューに、enqueue（イベント）されたら、サービスをコール（トリガ）してほしいなどのケースで用いる事ができます。

イベントに対して、トリガされた処理を行う、Azure Functions や AWS Lambdaなどが、馴染み深いと思いますが、これらと同じような感じでイベントに対して処理を行う仕組みを構成する事ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/c069d68e-5783-ed7c-b900-6e84208c27a9.png)

バインディングの一覧は、Input Binding (入力バインディング)、Output Binding (出力バインディング)共に以下に記載があります。
https://docs.dapr.io/reference/components-reference/supported-bindings/

## Cron (Scheduler)バインディングを試す

これらのInput Binding (入力バインディング)で、最もシンプルなのはCron (Scheduler)だと思いますので、まずはCron (Scheduler)から試していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/0b7472cb-d931-5c7d-8ecd-9d4436726d96.png)

### プロジェクトを作成、追加

今まで作業してきた、ソリューションのルートフォルダに移動して新たにプロジェクトを追加して、そのプロジェクトのコントローラーを対象にバインディングを設定します。

```bash
dotnet new webapi -o WorkerService
dotnet sln add WorkerService
```

### tye.yamlにプロジェクトを追加
忘れずにtye.yamlにもプロジェクトを追加して、daprをサイドカーとして起動するようにしておきましょう。ここでは、今までのYAMLにworker-serviceを追加している2行があります。

```YAML:tye.yaml
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
#
# when you've given us a try, we'd love to know what you think:
#    https://aka.ms/AA7q20u
#
name: dapr
extensions:
- name: dapr

  # log-level configures the log level of the dapr sidecar
  log-level: debug

  # config allows you to pass additional configuration into the dapr sidecar
  # config will be interpreted as a named k8s resource when deployed, and will be interpreted as
  # a file on disk when running locally at `./components/myconfig.yaml`
  #
  # config: myconfig

  # components-path configures the components path of the dapr sidecar
  components-path: "./components/"

  # If not using the default Dapr placement service or otherwise using a placement service on a nonstandard port,
  # you can configure the Dapr sidecar to use an explicit port.
  # placement-port: 6050
services:
- name: service-a
  project: ServiceA/ServiceA.csproj
- name: service-b
  project: ServiceB/ServiceB.csproj
- name: app
  project: App/App.csproj
- name: stateservice
  project: StateService/StateService.csproj
- name: worker-service
  project: WorkerService/WorkerService.csproj

# This may conflict with the redis instance that dapr manages.
#
# Doing a `docker ps` can show if its already running. If that's the case
# then comment out out when running locally. 
# - name: redis
#   image: redis
#   bindings: 
#   - port: 6379

```

### componentフォルダにcron-binding.yamlファイルを追加

statestoreを扱った際に、statestore.yamlを追加したと思いますが、同じようにcron-binging.yamlを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/99b9674b-931e-b594-feb9-e89e8ac73482.png)

```YAML:cron-binding.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: Job # 呼び出されるコントローラーパス(JobController)
spec:
  type: bindings.cron
  version: v1
  metadata:
  - name: schedule
    value: "@every 3s" # いわゆるCRON式です。* * *といったフォーマットも対応します。ここでは、3秒おきに実行しています。
scopes: # scopesでは、対象となるサービスを列挙して指定しています。
- worker-service
```

また、仕様を確認してみた所、POSTメソッドでの呼び出し固定となっています。
つまり、この場合/Job へPOSTの実装をJobControllerに行えばOKとなります。

### プロジェクトの変更

プロジェクトを変更します。まず、このWorkerServiceでは WeatherForecast.cs と WeatherForecastController.csは使わないので削除してください。そして、新しくJobController.csとJobResponse.csを用意しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/5218dd62-8f9a-e05f-c7b0-4f0495243d60.png)

また、Program.csも忘れずに編集しましょう。
特に//app.UseHttpsRedirection();は必ずコメントアウトしてください。Dapr起動時にリダイレクトが走った結果、エラーが発生します。

```csharp:Program.cs
using System.Diagnostics;
Activity.DefaultIdFormat = ActivityIdFormat.W3C;

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();

```

簡単ですが、レスポンス用のモデルを用意します。

```csharp:JobResponse.cs
namespace WorkerService;

public class JobResponse
{
    public DateTime Date { get; set; }

}

```

JobController では、POSTされたら、簡単にDatetime.Nowを詰めてレスポンスをしています。

```csharp:JobController 
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace WorkerService.Controllers;

[ApiController]
[Route("[controller]")]
public class JobController : ControllerBase
{
    private readonly ILogger<JobController> _logger;

    public JobController(ILogger<JobController> logger)
    {
        _logger = logger;
    }

    [HttpPost(Name = "PostJob")]
    public JobResponse Post()
    {
        JobResponse response = new JobResponse
        {
            Date = DateTime.Now
        };

        Console.WriteLine(response.ToString());
        return response;
    }
}

```

### 起動と確認

編集を終えたら、tye runで起動しましょう。

```bash
$ tye run
Loading Application Details...
Launching Tye Host...

[14:44:48 INF] Executing application from tye.yaml
[14:44:48 INF] Dashboard running on http://127.0.0.1:8000
[14:44:48 INF] Build Watcher: Watching for builds...
...
```

今までと同じ要領で、http://127.0.0.1:8000 をブラウザで開きましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/2c98c9de-6826-3ff5-6b63-7ad789451f09.png)

まず、worker-service-daprのログをViewから確認しましょう。
**name: Job, schedule fired**があれば、Daprはスケジュールジョブをキックできています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/8c2a859b-0e7a-5ddc-a894-f90a44a23419.png)

```
[worker-service-dapr_73c5055d-f]: time="2022-03-07T15:43:34.0106148+09:00" level=debug msg="name: Job, schedule fired: 2022-03-07 15:43:34.0106148 +0900 JST m=+141.014253101" app_id=worker-service instance=HX-90 scope=dapr.contrib type=log ver=1.6.0
```

worker-service側も確認してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/01569924-fb32-cda3-7ad2-a11e427b809a.png)
確認用にコンソールに出力していた結果が表示できていると思います。

```
[worker-service_75697214-b]: WorkerService.JobResponse
```