2021年4月30日金曜日

Kaleidox/Service

前回はソフトウェア開発の中軸モデル要素であるコンポーネントのメタモデルについて検討しました。

今回はコンポーネントの派生モデル要素であるサービスについて取り上げます。

サービスは外部に対して公開されたコンポーネントという位置付けなので、外部から直接実行できるコンポーネントということができます。

Kaleidoxでサービスモデルを直接実行することができるので、サービスモデルを使ってコンポーネントの特性を見ていきたい思います。

今回はサービスのAPIを定義して、Kaleidox上で実行してみます。

サービスモデル

足し算と引き算を行うサービスcalcを以下のように定義しました。この定義に沿って、サービスモデル(コンポーネントモデル)の定義について説明します。

* Service
** calc
*** Operation
**** plus
***** In
| 名前 | 型  | 多重度 |
|------+-----+--------|
| arg1 | int |      1 |
| arg2 | int |      1 |
***** Out
| 型  | 多重度 |
|-----+--------|
| int |      1 |
***** Method
+ arg1 arg2
**** minus
***** In
| 名前 | 型  | 多重度 |
|------+-----+--------|
| arg1 | int |      1 |
| arg2 | int |      1 |
***** Out
| 型  | 多重度 |
|-----+--------|
| int |      1 |
***** Method
****** js
arg1 - arg2

全体構造

サービスモデルの全体構造は以下のようになっています。

* Service
** サービス名
*** Operation
**** オペレーション名
***** In
***** Out
***** Method

Service節でサービスモデルの定義を行います。

Service節の直下にはサービス名を節名としたサービスの定義が並びます。

サービス名節の直下にはOperation節があります。

Operation節の直下にはオペレーション名を節名としたオペレーションの定義が並びます。

オペレーションの定義は次節で説明します。

拡張予定

前回、コンポーネントの部品として検討した部品の中でAPIに該当する部品をサポートしています。

以下の部品は現在は未実装です。

  • SPI
  • Bus
  • StateMachine
  • Entity
  • Configuration
  • Rule
  • Log
  • Metrics
  • Audit trails

これらのモデル要素はkaleidox上で順次実現していく予定です。

オペレーション

plusオペレーションの定義は以下になります。

*** Operation
**** plus
***** In
| 名前 | 型  | 多重度 |
|------+-----+--------|
| arg1 | int |      1 |
| arg2 | int |      1 |
***** Out
| 型  | 多重度 |
|-----+--------|
| int |      1 |
***** Method
+ arg1 arg2

オペレーションの定義は以下の3つです。

In
入力定義
Out
出力定義
Method
Method定義

In節には入力情報を表形式で記述します。表では入力パラメタの名前、型、多重度を定義します。

Out節には出力情報を表敬式で記述します。表では出力パラメタの型、多重度を記述します。

Method節にはオペレーションの実装であるメソッドの定義を行います。

メソッド

Method節では、オペレーションの実装であるメソッドの定義を行います。

モデルが仕様のみで実装まで定義しない場合はMethod節を省略することができます。

ただし、Method節を省略した場合はKaleidoxでの直接実行はできません。手動で作成したプログラムを、SimpleModelerのプログラム自動生成と組み合わせて使用する形になります。

Kaleidox

オペレーションの実装をKaleidoxスクリプトで定義する場合は、Method節に直接Kaleidoxスクリプトを記述します。

サービスモデルのplusオペレーションの下には、以下のMethod節を定義しています。

***** Method
+ arg1 arg2

calcサービスのplusオペレーションが呼び出されると、Kalidoxスクリプト「+ arg1 arg2」が実行され、その結果が返却されます。

JavaScript

Method節の下にjs節が配置されている場合は、js節内をMethod実現のためのJavaScriptスクリプトとして使用します。

***** Method
****** js
arg1 - arg2

calcサービスのminusオペレーションが呼び出されると、JavaScriptスクリプト「arg1 - arg2」が実行され、その結果が返却されます。

Java ScriptEngine

Method節の下には、JavaScript以外にもJava ScriptEngineでサポートしているスクリプト言語を使用することができます。この場合は、Java ScriptEngineの認識する言語名を節名で指定します。

拡張予定

メソッドの実現方法として、外部のJava/Scalaプログラムを利用可能にする予定です。以下の形式が候補となっています。

  • JavaBeans (JAR)
  • Servlet (WAR)
  • OSGi

実行

それでは、Kaleidoxでサービスモデルを直接実行してみます。

前述のサービスモデルをinit.kldに格納してkaleidoxが認識できるようにします。

コマンド

まず、コマンドラインで実行してみます。

$ klaidox calc.plus 1 2
3

サービス名とオペレーション名を「.」で繋いだ文字列でオペレーション名を指定します。calcサービスのplusオペレーションなので「calc.plus」となります。

引数には「1」と「2」を指定した結果、計算結果の3が返されました。

REPL

次はKaleidoxのREPLで実行してみます。

REPLのプロンプトから「calc.plus 1 2」と実行すると、計算結果の3が返されました。

kaleidox> calc.plus 1 2
3

トレース

calc.plusの実行時のコールツリーは、REPLコマンド「:trace」で表示することができます。先程の「calc.plus 1 2」の場合は以下になります。

kaleidox> :trace
INVOKE(302)[Script] (calc.plus 1 2) => 3
  INVOKE(104)[Function(Operation):service-script-kaleidox(calc.plus)] (calc.plus 1 2) => 3
    INVOKE(60)[Lambda:calc.plus] (1 2) => 3
      INVOKE(53)[Script] (+ arg1 arg2) => 3
        INVOKE(23)[Function(Eval):+] (+ arg1 arg2) => 3
          INVOKE(15)[Variable:arg1] arg1 => 1
          INVOKE(1)[Variable:arg2] arg2 => 2

Web REST

KaleidoxをWebで実行するためのコンテナとしてpbjを開発しました。

  • https://github.com/asami/pbj

pbjを実行するとWebサーバーが立ち上がります。ポート番号は8080です。

$ pbj

curlコマンドでpbjサーバーを呼び出します。

パス名はサービス名「calc」、オペレーション名「plus」をつなげた「/calc/plus」です。引数にはarg1、arg2にそれぞれ「1」、「2」を指定します。

$ curl "http://localhost:8080/calc/plus?arg1=1&arg2=2"
{"status":200,"data":3}

実行の結果、JSONの形式で実行結果が返されました。プロパティdataに計算結果の「3」が入っています。

jqコマンドを併用してJSONを整形すると、以下のようになります。

$ curl "http://localhost:8080/calc/plus?arg1=1&arg2=2" | jq .
{
  "status": 200,
  "data": 3
}

トレース

Webサーバ上で実行したときのコールツリーは「_trace」パラメタを「true」にすると取得することができます。

$ curl "http://localhost:8080/calc/plus?arg1=1&arg2=2&_trace=true"  | jq
{
  "status": 200,
  "data": 3,
  "trace": {
    "kind": "root",
    "trace": [
      {
        "kind": "invoke",
        "timestamp": "1619340084576",
        "label": "Script",
        "enter": "(calc.plus :arg1 1 :arg2 2)",
        "leave": "3",
        "lap": 8,
        "trace": [
          {
            "kind": "invoke",
            "timestamp": "1619340084577",
            "label": "Function(Operation):service-script-kaleidox(calc.plus)",
            "enter": "(calc.plus :arg1 1 :arg2 2)",
            "leave": "3",
            "lap": 6,
            "trace": [
              {
                "kind": "invoke",
                "timestamp": "1619340084578",
                "label": "Lambda:calc.plus",
                "enter": "(1 2)",
                "leave": "3",
                "lap": 5,
                "trace": [
                  {
                    "kind": "invoke",
                    "timestamp": "1619340084581",
                    "label": "Script",
                    "enter": "(+ arg1 arg2)",
                    "leave": "3",
                    "lap": 2,
                    "trace": [
                      {
                        "kind": "invoke",
                        "timestamp": "1619340084582",
                        "label": "Function(Eval):+",
                        "enter": "(+ arg1 arg2)",
                        "leave": "3",
                        "lap": 1,
                        "trace": [
                          {
                            "kind": "invoke",
                            "timestamp": "1619340084582",
                            "label": "Variable:arg1",
                            "enter": "arg1",
                            "leave": "1",
                            "lap": 0
                          },
                          {
                            "kind": "invoke",
                            "timestamp": "1619340084582",
                            "label": "Variable:arg2",
                            "enter": "arg2",
                            "leave": "2",
                            "lap": 1
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}

まとめ

サービスのモデルを作成し、Kaleidoxで直接実行してみました。今回はAPIのみでしたが、コンポーネントの回に検討したBusなどのメカニズムとそのモデルについても順次実現していく予定です。

同じモデルがコマンドライン、REPL、Webのそれぞれでそのまま実行できることを確認できました。モデルを直接実行できれば、プログラミングの工数削減、仕様と実装のズレの低減といった大きなメリットがあります。

また、Kaleidoxのようなモデル実行プラットフォームが提供するアプリケーションフレームワーク機能により、ドメインモデルではスコープ外の非機能要件についても自動的に利用可能になるメリットもあります。

今回はKaleidoxが提供する機能であるトレース機能を紹介しました。実運用では、本番環境でしか発生しない障害などが発生することが多々ありますが、トレース機能を用いると効率的に障害調査を行うことができることを期待できます。

諸元

Kaleidox
0.1.16
Pbj
0.0.2