Daprのクイックスタートでサービス呼出しのイメージを掴む

# はじめに

Daprが提供する機能の一つに、サービス呼出し(Service Invocation)があります。これは、Daprが立ち上げたサービスをHTTPやgRPCで呼び出して、サービス間の連携をサポートするものです。この機能を試すのに良いサンプルが、Dapr公式のHello Worldクイックスタートになります。
https://github.com/dapr/quickstarts/tree/master/hello-world

このクイックスタートのREADMEは、非常に丁寧に書かれていて、分かりやすいドキュメントになっていると感じます。基本的に書かれている内容に沿ってコマンドを実行していくと問題なく動作しました。本記事では、このクイックスタートを題材に、Daprの主要な機能であるサービス呼出し機能を説明します。また、このクイックスタートではDaprの状態管理機能も使っているので、併せて概要を説明したいと思います。

# 学べること

- Daprを使ったサービス呼出し(Service Invocation)のイメージ
- HTTPを使ったDaprのサービス呼出しの方法
- 以下のDapr CLIコマンドの簡単な使い方  
dapr run / dapr stop / dapr invoke
- Daprによる状態管理(State management)のイメージ

**※これまでに投稿したDaprに関する記事**

- [マイクロサービスを簡単に連携させるランタイム「Dapr」について](https://qiita.com/highitoh/items/8c1643fb5d6d7634a5f9)

# 構成

READMEの中で、唯一全体構成の説明が少なかったので、ここで補足します。Hello Worldの構成は以下のようになっています。**nodeappサービスがサーバ、pythonappサービスがクライアント**として振る舞うシンプルな構成です。名前から想像できるとおり、nodeappサービスはnodeのアプリケーション、pythonappサービスはpythonのアプリケーションが動いています。

nodeappサービスにはneworderとorderの2つのメソッドが定義されています。これらのメソッドで、新しい注文情報の登録と、登録した注文情報の参照を行います。neworderのメッセージを受け取ると、nodeappサービスはそれに格納された注文番号を保存します。また、orderのメッセージを受け取ると、nodeappサービスは保存した注文番号を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2243249/46669077-abd8-96a8-863c-0bbc0c2138e8.png)

# Hello Worldのサービス呼出し

ここで、Hello WorldクイックスタートのStepを追いながら、Daprによるサービス呼出しのポイントについて説明したいと思います。

## Step 1 Daprのセットアップ

Hello Worldの実行のためには、あらかじめDockerとNode、Pythonをインストールする必要があります。続けて、**Dapr CLI**のインストールと初期化を行います。ステップ数も少なく、ドキュメント通りにエラーなく実行できました。
https://docs.dapr.io/getting-started/install-dapr-cli/
https://docs.dapr.io/getting-started/install-dapr-selfhost/

## Step 2 コードの説明

Step2では、Hello Worldで使うnodeアプリのコードが説明されています。

先述のとおり、nodeappでは二つのメソッドが定義されます。一つはHTTP POSTリクエストでjsonを受け取ってorderを登録するneworderメソッド、もう一つはHTTP GETリクエストを受け取り保存したorderを表示するorderメソッドです。nodeアプリは、3000番ポートをリッスンしてリクエストをDaprから受け取ります。

nodeappは、上のHTTPリクエストを受け取ると注文情報の保存と参照を行います。注文情報の保存と参照の詳細については、「Hello Worldの状態管理」にて説明します。

## Step 3 Daprでnodeアプリを起動する

**dapr run**コマンドを使うと、Daprでアプリケーションを起動できます。

```
dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 node app.js
```

上のコマンドでは、3つのフラグを設定しています。まず、起動対象のアプリのIDを指定します(--app-id nodeapp)。また、nodeアプリがリッスンしているポート番号を指定し(--app-port 3000)、DaprがHTTPをリッスンするポート番号を3500に指定しています(--dapr-http-port 3500)。なお、DaprのHTTPのリッスンポートは3500なので、最後のフラグは無くても動作します。
最後に、アプリを起動するコマンドとして"node app.js"を入力しています。

このコマンドを実行すると、Daprでnodeappサービスが立ち上がります。

## Step 4 アプリにPOSTメッセージを送る

**dapr invoke**コマンドを使って、アプリで定義されたメソッドを実行することができます。

```
dapr invoke --app-id nodeapp --method neworder --data-file sample.json
```

上のコマンドでは、nodeアプリ(--app-id nodeapp)に定義されたneworderメソッド(--method neworder)を呼出しています。invokeコマンドではjsonを入力することができ、入力するjsonファイルを別途指定しています(--data-file sample.json)。

このコマンドを実行すると、HTTP POSTリクエストでnodeappサービスのneworderメソッドが呼び出されます。nodeappは、送信された注文情報から注文番号を取り出し、保存します。

## Step 5 アプリにGETメッセージを送る

Step 4と同様に、dapr invokeコマンドを使ってGETメッセージをアプリに送ることができます。orderメソッドはGETリクエストを受け付けるため、verbフラグでGETを指定しています(デフォルトはPOST)。

```
dapr invoke --app-id nodeapp --method order --verb GET
```

このコマンドを実行すると、HTTP GETリクエストでnodeappサービスのorderメソッドが呼び出されます。nodeappは、保存された注文情報を参照し、注文番号を返します。

## Step 6 DaprでPythonアプリを起動する

Step 3と同様に、dapr runコマンドでPythonアプリを起動します。Pythonアプリの実行なので、最後のコマンドが"python3 app.py"となっていますね。

```
dapr run --app-id pythonapp python3 app.py
```

このコマンドを実行すると、Daprでpythonappサービスが立ち上がります。pythonappサービスは、HTTP POSTリクエストを使ってnodeappサービスのneworderメソッドを1秒おきに呼出します。

## Step 7 アプリの停止

**dapr stop**コマンドを使って、起動したアプリを停止できます。フラグで停止したいアプリのIDを指定します。

```
dapr stop --app-id nodeapp
dapr stop --app-id pythonapp
```

このコマンドを実効すると、nodeappサービスとpythonappサービスが停止します。

# Hello Worldの状態管理

Hello Worldでは、nodeappサービスは**DaprのState Managementの機能を使って、Key-Valueデータとして注文情報を保存しています。**データの保存先は、**componentsディレクトリ**の**statestore.yaml**ファイルに定義されています。Linuxのcomponentsディレクトリのデフォルトは、$HOME/.dapr/componentsです。

筆者の環境にて、statestore.yamlのデフォルトの内容は下のようになっていました。metadata.nameでデータの保存先の名前を"statestore"に指定し、spec.typeで保存先としてRedisを利用することを指定しています。

```
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
...
```

nodeアプリは、保存先の名前(statestore)をキーとして、状態データとして保存した注文情報にアクセスします。**状態データはstate API**を使ってアクセスすることができます。

nodeアプリは、HTTP POSTリクエストでneworderメソッドが呼び出されると、下のHTTP POSTリクエストでDaprのstate APIを叩いて注文情報を保存します(bodyに登録するjsonデータを格納)。

```
POST http://localhost:3500/v1.0/state/statestore
```

state APIの状態保存リクエストの構文は下のとおりです。構文は、DaprドキュメントのState management API referenceに記載されています。

```
POST http://localhost:<daprPort>/v1.0/state/<storename>
```

また、HTTP GETリクエストでorderメソッドが呼び出されると、下のHTTP GETリクエストで同じくstate APIを叩いて注文情報を取得します。

```
GET http://localhost:3500/v1.0/state/statestore/order
```

state APIの状態取得リクエストの構文は下のとおりなので、statestoreからキーがorderの値を取得していることが分かります。

```
GET http://localhost:<daprPort>/v1.0/state/<storename>/<key>
```

# おわりに

Daprが提供するサービス呼出し機能を、このクイックスタートで簡単に確認することができます。異なる言語でサービスを書いていても、Daprがその違いを吸収してサービスを立ち上げ、シームレスにサービスを連携できることが実感できました。
Daprに関しては継続的に記事を書いていきたいと思っています。これまでに書いた記事を下に載せていますので、参考になれば嬉しいです。

- [マイクロサービスを簡単に連携させるランタイム「Dapr」について](https://qiita.com/highitoh/items/8c1643fb5d6d7634a5f9)

# 参考リンク
- GitHub - Dapr quickstarts Hello World  
https://github.com/dapr/quickstarts/tree/master/hello-world
- Dapr CLI Reference  
https://docs.dapr.io/reference/cli/cli-overview/
- State management API reference  
https://docs.dapr.io/reference/api/state_api/