知らない言語を使ってみたい - ballerina 編 ( Microservice 志向な言語 )

どこか遠くへ行きたい

第 4 弾は、ballerina の体験です。

# ballerina

<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/9b56cc67-8526-901e-23f0-52f5e73e126b.png" width="200px" />

[公式サイト : https://ballerina.io/](https://ballerina.io/)
[公式FAQ : https://ballerina.io/learn/faq/](https://ballerina.io/learn/faq/)
[API ドキュメント : https://ballerina.io/learn/api-docs/](https://ballerina.io/learn/api-docs/ballerina/http.html)
[リポジトリ : https://github.com/ballerina-platform/ballerina-lang](https://github.com/ballerina-platform/ballerina-lang)
[Examples 一覧 - https://ballerina.io/learn/by-example/](https://ballerina.io/learn/by-example/)
[Guides 一覧 - https://ballerina.io/learn/by-guide/](https://ballerina.io/learn/by-guide/)

[◆ philosophy ◆](https://ballerina.io/philosophy/)


> Ballerina makes it easy to write cloud native applications while maintaining reliability, scalability, observability, and security.

> Ballerina is a compiled, transactional, statically and strongly typed programming language with textual and graphical syntaxes. Ballerina incorporates fundamental concepts of distributed system integration into the language and offers a type safe, concurrent environment to implement microservices with distributed transactions, reliable messaging, stream processing, and workflows.

|  |        | 
|-----------------:|------------------|
| 用途 | 汎用 | 
| 開始 | 2014/04 | 
| 最新バージョン | 0.990.0 ( 2018/10/13 )| 
| ライセンス | Apache License Version 2.0 |
| プラットフォーム | JVM |
| 型 | 静的型付け | 
| パッケージ管理 | 標準搭載 | 
| Githubスター | 1548 ( 2018/12/18 ) |


- Microservice 志向
    - 各種ネットワークプロトコルへの標準対応
        - gRPC
        - HTTP 2
        - WebSockets
        - Messaging
            - AMPQ
            - Kafka
        - RESTful
            - Open API ( Swagger ) 
    - デプロイメント
        - Artifact 生成
            - Dockerfile
            - Docker image
            - Kubernetes Resources
    - セキュリティ対策
        - Authentication 対応
            - Base Auth
            - jwt
            - OAuth (client only)
        - Sanitizing 強制アノテーション
    - サービスメッシュ機能を標準搭載
        - Circuit Breaker, Failover, Retry, Load Barancing...
        - Istio との共存も可能
    - Prometheus に標準対応
    - OpenTrace に標準対応
        - Zipkin, Jaeger
    - Elastic Stack への対応 ( 微妙 )
- Visual Programming
- 並列・非同期処理

2018/12/18 現在、Commit が 56,988、Closed Issue が 3,710、Open Issue が 584、Closed Pull Request が 8,497、 Open Pull Request が 40、 [リリースページの熱量も半端ない](https://github.com/ballerina-platform/ballerina-lang/releases)。`babel/babel` に比肩する盛況ぶり。

# インストール

・Windows 10 Pro 1803
・Docker for Windows 18.09.0
・Kubernetes ( docker-for-desktop ) v1.10.3
・kubectl v1.10.3
・heml v2.12.0

インストーラが提供されているので、ダウンロードしてインストールを行う。
https://ballerina.io/downloads/

一つ気を付ける必要があるのが、JDK 1.8 にしか対応していない事。
インストーラには JRE が含まれているので問題ないが、Zip ファイルを展開した場合は別途インストールが必要。

```powershell
$ ballerina -v
# Ballerina 0.990.0
```

# エディタ支援

とても充実している
https://ballerina.io/learn/tools-ides/

VS Code を使うのであれば、以下拡張が使える。
https://marketplace.visualstudio.com/items?itemName=ballerina.ballerina

オートコンプリートや定義ジャンプもでき、デバッガも用意されている。

また、その大きな特徴として、**Diagram View** があること。
![2018-12-18_18h48_59.png](https://qiita-image-store.s3.amazonaws.com/0/20198/470ab7c9-6c97-b59b-9d86-c72c9dc13c83.png)

# 開発の流れ

以下より重要そうなトピックをピックアップしながら進める。
https://ballerina.io/learn/

## 初期化

```powershell
$ ballerina init
# Ballerina project initialized

$ tree -a
# .
# ├── .ballerina
# │  └── .gitignore
# ├── .gitignore
# ├── Ballerina.toml
# └── hello_service.bal
```

`Ballerina.toml` がプロジェクト管理ファイルのようだ。

```toml:Ballerina.toml
[project]
org-name = "user"
version = "0.0.1"
```

詳細は以下
https://ballerina.io/learn/how-to-structure-ballerina-code/

## 実行・ビルド

`init` でできたファイルは以下である ( コメントは削除した )。

```php:hello_service.bal
import ballerina/http;

service hello on new http:Listener(9090) {
    resource function sayHello(http:Caller caller, http:Request request) {
        http:Response response = new;
        response.setTextPayload("Hello Ballerina!");
        _ = caller -> respond(response);
    }
}
```

実行のみであれば、以下コマンドでできる。

```powershell
$ ballerina run .\hello_service.bal
# Initiating service(s) in 'hello_service.bal'
# [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9090
```

```powershell
$ curl http://localhost:9090/hello/sayHello
# Hello Ballerina!
```

また、ビルドをする場合は以下。

```powershell
# build
$ ballerina build

$ tree
# ├── Ballerina.toml
# ├── hello_service.bal
# └── target
#    ├── Ballerina.lock
#    └── hello_service.balx
```

`target/hello_service.balx` が生成されたバイナリとなっている。

```powershell
$ ballerina run .\target\hello_service.balx
# Initiating service(s) in 'hello_service.bal'
# [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9090
```
## ダイアグラム

VS Code か IntelliJ を使えば、ダイアグラムを使った Visual Programming が可能。
![vp.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/ed263438-eb35-5dc6-d327-ebbd7a95d050.gif)

こんな感じで色々とできる。
今の所あまり自由度が高いとは言えず、基本はコードの全体像を把握するためのツールと捉えたほうが良さそう。

詳細は以下
https://ballerina.io/learn/tools-ides/

## デバッガ

VS Code と IntelliJ には、デバッガーも用意されている。
![2018-12-19_12h02_34.png](https://qiita-image-store.s3.amazonaws.com/0/20198/ad69e0df-459d-2982-521b-5cfd69e19819.png)

## アノテーション

ballerina は、様々なアノテーションを付与することで、service や endpoint, resource のカスタマイズができる。

```diff:hello_service.bal
import ballerina/http;

+ @http:ServiceConfig {
+    basePath: "/api"
+ }
service hello on new http:Listener(9090) {
+     @http:ResourceConfig {
+         methods: ["POST"],
+         path: "/say"
+     }
    resource function sayHello(http:Caller caller, http:Request request) {
        http:Response response = new;
        response.setTextPayload("Hello Ballerina!");
        _ = caller -> respond(response);
    }
}
```

```powershell
$ ballerina run .\hello_service.bal
# Initiating service(s) in 'hello_service.bal'
# [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9090
```

```powershell
$ curl http://localhost:9090/api/say -X POST
# Hello Ballerina!
```

## 設定ファイル

`ballerina/config` を使えば、標準で設定ファイルが読み込める。

```toml:myconfig.toml
access_token = "qwertyuiop"
```

```diff:hello_service.bal
import ballerina/http;
+ import ballerina/config;

@http:ServiceConfig {
   basePath: "/api"
}
service hello on new http:Listener(9090) {
    @http:ResourceConfig {
        methods: ["POST"],
        path: "/say"
    }
    resource function sayHello(http:Caller caller, http:Request request) {
        http:Response response = new;
        response.setTextPayload(config:getAsString("access_token"));
        _ = caller -> respond(response);
    }
}
```

```powershell
$ ballerina run --config myconfig.toml hello_service.bal
...

$ curl http://localhost:9090/api/say -X POST
# qwertyuiop
```

## モジュール

モジュールを追加する場合は、結構簡単にできる。

https://ballerina.io/learn/how-to-structure-ballerina-code/

自作モジュールは、こんな感じに作れる。

```powershell
$ mkdir math
$ cd math
$ touch add.bal
$ tree 
# 
```

```php:math\add.bal
public function addTwice(int a, int b) returns int {
  return a + a + b + b;
}
```

```diff:hello_service.bal
...
+ import user/math;
...

service hello on cmdListener {
...
+     resource function add(http:Caller caller, http:Request request) {
+         http:Response response = new;
+         response.setTextPayload(<string>math:addTwice(10, 30));
+         _ = caller -> respond(response);
+     }
}
```

```powershell
$ ballerina run hello_service.bal
...

$ curl http://localhost:9090/api/add
# 80
```

`import user/math` の `user` は、`Ballerina.toml` の `project.org-name` から取られる。

詳細は以下
https://ballerina.io/learn/how-to-structure-ballerina-code/

## 公開モジュール

ballerina では、 Ballerina Central というモジュールを共有する公開リポジトリが提供されている。
https://central.ballerina.io/

公開されているモジュールを利用するには、`ballerina pull` で取得する必要がある。
取得されたモジュールは、`~/.ballerina/cache` 以下に展開される。

```powershell
$ ballerina search github
# Ballerina Central
# =================
# 
# |NAME            | DESCRIPTION                   | DATE           | VERSION |
# |----------------| ------------------------------| ---------------| --------|
# |wso2/github4    | Connects to the GitHub Grap...| 2018-12-08-土   | 0.9.19  |
# |shan1024/chalk  | Bring colors to Ballerina p...| 2018-09-29-土   | 0.1.6   |
# |mirage/os       | Run OS system commands.       | 2018-08-10-金   | 0.1.0   |
# |chanakal/comm...| Generates WSO2 Committer Re...| 2018-12-09-日   | 0.9.12  |
# |maryamzi/github | A basic webhook accepting G...| 2018-08-06-月   | 0.0.3   |
# |pasanw/wordpr...| Connects to Wordpress from ...| 2018-09-28-金   | 0.0.6   |
# |anuruddha/kub...| Ballerina connector for Kub...| 2018-08-10-金   | 0.0.2   |
# |wso2/ethereum   | Connects to Ethereum from B...| 2018-12-10-月   | 0.8.5   |
# |hemikak/strin...| String utility functions fo...| 2018-08-01-水   | 0.0.4   |

$ ballerina pull wso2/github4
# wso2/github4:0.9.19 [central.ballerina.io -> home repo]
```

あとは、`import` すれば利用できる。

```diff:hello_service.bal
import ballerina/http;
import ballerina/config;
+ import wso2/github4;
...

```

自分が利用しているモジュール一覧は、`ballerina list` で確認可能

```powershell
$ ballerina list
# Compiling source
#     hello_service.bal
# hello_service.bal
# ├── ballerina/http
# │   ├── ballerina/internal
# │   │   ├── ballerina/time
# │   │   └── ballerina/log
# │   ├── ballerina/system
# │   ├── ballerina/config
# │   │   └── ballerina/system
# │   ├── ballerina/math
# │   ├── ballerina/crypto
# │   ├── ballerina/mime
# │   │   ├── ballerina/io
# │   │   └── ballerina/file
# │   ├── ballerina/file
# │   ├── ballerina/time
# │   ├── ballerina/io
# │   ├── ballerina/runtime
# │   ├── ballerina/cache
# │   │   ├── ballerina/time
# │   │   ├── ballerina/task
# │   │   └── ballerina/system
# │   ├── ballerina/log
# │   ├── ballerina/reflect
# │   └── ballerina/auth
# │       ├── ballerina/system
# │       ├── ballerina/time
# │       ├── ballerina/log
# │       ├── ballerina/internal
# │       │   ├── ballerina/time
# │       │   └── ballerina/log
# │       ├── ballerina/cache
# │       │   ├── ballerina/time
# │       │   ├── ballerina/task
# │       │   └── ballerina/system
# │       ├── ballerina/runtime
# │       └── ballerina/config
# │           └── ballerina/system
# └── ballerina/config
#     └── ballerina/system
```

また、自分の作ったモジュールをアップロードするのも簡単。

```powershell
$ ballerina push math
```

詳細は以下
https://ballerina.io/learn/how-to-publish-modules/

## テスト

テストも標準で提供されている。Testerina と呼ばれる。
先程の `math` モジュールにテストを追加する。

```

```powershell
$ mkdir .\math\tests
$ touch .\math\tests\addTest.bal
```

```php:math\tests\addTest.bal
import ballerina/io;
import ballerina/test;

@test:BeforeSuite
function beforeFunc() {
    io:println("called once before all tests");
}

@test:BeforeEach
function beforeEachFunc() {
    io:println("called before every test");
}

@test:Config
function testFunction() {
    test:assertEquals(addTwice(10, 40), 100 , msg = "Failed");
}

@test:AfterSuite
function afterFunc() {
    io:println("called once after all tests");
}
```

```powershell
$ ballerina test math
# Compiling tests
#     user/math:0.0.1
# 
# Running tests
#     user/math:0.0.1
# called once before all tests
# called before every test
# called once after all tests
#         [pass] testFunction
# 
#         1 passing
#         0 failing
#         0 skipped
```

ソースファイル内に直接記述もできるので、そっちのほうが便利かも。

```php:math\add.bal
import ballerina/test;

public function addTwice(int a, int b) returns int {
  return a + a + b + b;
}

@test:Config
function testFunction() {
    test:assertEquals(addTwice(10, 40), 100 , msg = "Failed");
}
```

詳細は以下
https://ballerina.io/learn/how-to-test-ballerina-code/

## ドキュメント

コードのドキュメント記述には、Docerina という拡張 Markdown で記述する。

```php:math\add.bal
# Add given params twice
#
#
#
# + a - One number
# + b - Other number
# + return - Added number
#
public function addTwice(int a, int b) returns int {
  return a + a + b + b;
}
```
![2018-12-19_11h14_41.png](https://qiita-image-store.s3.amazonaws.com/0/20198/a3e2c27c-cb8a-7071-1899-c796f8212a1c.png)

また、HTML でドキュメントを出力も可能です。

```powershell
$ ballerina doc
# docerina: API documentation generation for sources - [math]
# docerina: HTML file written: C:\sample_1219\target\api-docs\math.html
# docerina: HTML file written: C:\sample_1219\target\api-docs\index.html
# docerina: HTML file written: C:\sample_1219\target\api-docs\module-list.html
```

```powershell
$ cd .\target\api-doc\
$ armor
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/35a7c234-7943-fa39-5bea-0dd7b77f1fcc.png" width="70%" />

モジュールフォルダに `Module.md` を置いておくと、それも読み込んでくれる。
上記画像でいくと、`マスモジュールです。` の所がそれだ。

詳細は以下
https://ballerina.io/learn/how-to-document-ballerina-code/

## Docker

Docker に対するアノテーションを追加することで、ビルド時に Dockerfile を自動生成し、 Docker build してくれる。

```diff:hello_service.bal
import ballerina/http;
import ballerina/config;
import ballerinax/docker;

+ @docker:Config {
+     name:"helloworld",
+     tag:"v1.0"
+ }
+ @docker:CopyFiles {
+     files:[
+         {source:"./myconfig.toml", target:"/home/ballerina/conf/myconfig.toml", isBallerinaConf:true}
+     ]
+ }
+ @docker:Expose {}
+ listener http:Listener cmdListener = new(9090);


@http:ServiceConfig {
   basePath: "/api"
}
- service hello on new http:Listener(9090) 
+ service hello on cmdListener {
    @http:ResourceConfig {
        methods: ["POST"],
        path: "/say"
    }
    resource function sayHello(http:Caller caller, http:Request request) {
        http:Response response = new;
        response.setTextPayload(config:getAsString("access_token"));
        _ = caller -> respond(response);
    }
}
```

```powershell
$ ballerina build hello_service.bal
# Compiling source
#     hello_service.bal
# Generating executable
#     ./target/hello_service.balx
#         @docker                  - complete 3/3
# 
#         Run the following command to start a Docker container:
#         docker run -d -p 9090:9090 helloworld:v1.0

$ docker images
# REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
# helloworld                                 v1.0                4bfa8d7b4f54        38 seconds ago      128MB
# ballerina/ballerina-runtime                0.990.0             5d1249acb988        8 days ago          128MB
# ...

$ docker run -d -p 9090:9090 helloworld:v1.0
# 02d446fc149b9cdf8e3d5327ac24be46affc4fa6a2f475f2f8122e72669ec39e

$ docker ps
# CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
# 02d446fc149b        helloworld:v1.0     "/bin/sh -c 'balleri…"   6 seconds ago       Up 3 seconds        0.0.0.0:9090->9090/tcp   hardcor e_burnell

$ curl http://localhost:9090/api/say -X POST
# qwertyuiop
```

生成物を確認すると、

```powershell
$ tree target
# target
# ├── Ballerina.lock
# ├── hello_service
# │  ├── Dockerfile
# │  └── myconfig.toml
# └── hello_service.balx
```

```dockerfile:.\target\hello_service\Dockerfile
# Auto Generated Dockerfile

FROM ballerina/ballerina-runtime:0.990.0
LABEL maintainer="dev@ballerina.io"

COPY hello_service.balx /home/ballerina 

COPY myconfig.toml /home/ballerina/conf/myconfig.toml
EXPOSE  9090
CMD ballerina run  --config /home/ballerina/conf/myconfig.toml hello_service.balx
```

ベースイメージが `ballerina/ballerina-runtime` になっている。

Docker for Windows を利用している場合は、以下の設定をしないと ballerina から繋げない。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/36926847-ec4b-4c5a-95bc-17914d66cfa8.png" width="50%" />
こんなエラーが出たら、DNS Server を固定すると解決できる。
![2018-12-19_07h28_58.png](https://qiita-image-store.s3.amazonaws.com/0/20198/e7c0755b-f8ff-dcbe-446e-4b850b04e532.png)
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/8cea53ce-405c-cd42-3d49-e9fa562cfbaf.png" width="50%" />

# デザインプリンシプル

ここまでで、おおよその開発フローは理解できたので、ここからはもう少しコンセプトについて深めていく。

## Microservice

ballerina は Microservice 志向であり、多数のサービス間でネットワーク通信をしながら協調して動作するように設計されている。

### ● Protocol

以下機能がデフォルトで提供されているネットワークプロトコルだ。

- HTTP, HTTPS
    - HTTP 1.1
    - HTTP 2.0
    - 一部認証へも対応
        - Basic 認証
        - JWT
        - OAuth (クライアントのみ)
        - LDAP ?
- WebSocket
- WebSub
- Database
- gRPC
- AMPQ (JMS)

### ● Traffic Management

また、サービスメッシュを構築するための機能も提供されている。

- クライアント
    - Circuit Breaker
    - Load Balancing
    - Timeout, Retry, Failover
    - Transaction ( Experimental )

Istio が担う範囲までアプリケーションでやるつもりなんだろうか。

> https://ballerina.io/learn/faq/#how-does-ballerina-compare-to-a-service-mesh-like-istio
> #### How does Ballerina compare to a Service Mesh like Istio?
> 
> Service meshes exist to make it easier to write resilient distributed systems. They apply transaction resilience at the network request level and Ballerina applies it within the logic level. Ballerina works both with and without a service mesh! In situations where a service mesh is not present, Ballerina provides network bridging and transaction management for invocations in between services written with Ballerina or integrated via the Ballerina Bridge. In situations where a service mesh already exists, Ballerina services can be configured to delegate routing and transaction capabilities to the underlying mesh.

ということなので、Istio とは共存していくんだろうと。

## Security

ネットワークには付き物のセキュリティについても配慮がなされている。
https://ballerina.io/learn/how-to-write-secure-ballerina-code/

### ● untrusted data

データを外部から受け取って処理をする以上、Injection や Path Manipulation の危険性からは逃れられない。
ballerina では、データの信頼性を表すアノテーションをデータに付与し、危険な操作をコンパイル時に弾くことができる。

**@sensitive**

Database に SQL を発行する `select` 関数は、引数の `sqlQuery` に `@sensitive` アノテーションが付与されている。

```php
public extern function select(@sensitive string sqlQuery, typedesc? recordType,
                              boolean loadToMemory = false, Param... parameters)
                             returns @tainted table|error;
```

これにより、`sqlQuery` には信頼できないデータは入れられない様になっている。
では、信頼できないデータとは何か。

**@tainted**

信頼できないデータには、`@tainted` アノテーションが付与される。
先程の `select` メソッドの戻り値が `@tainted` になっている。これは、SQL で取得したデータが安全ではないと考えれば当然。

この様に、外部から受け取ったデータは基本的に全て信頼できないデータとなる。

**@untainted**

しかし、外部から受け取ったデータは全て `@sensitive` に使えないと困るので、その場合はサニタイズを行い怪しさを洗浄する事でまた信頼を取り戻すことができる。

```php
function sanitizeSortColumn (string columnName) returns @untainted string {
   string sanitizedSortColumn = columnName;
   // Sanitize logic to make sure return value is safe
   return sanitizedSortColumn;
}
```

この関数が返した `@untainted` な文字列は、`@sensitive` な引数へも渡すことができるようになる。
しかし、サニタイズ処理はユーザに任されているのはどうなんだろう。標準パッケージで一通りのサニタイザが提供されてるんだろうか。

### ● Secrets

接続情報や Token, API Key のような Secrets を使う場面は多い。
その時は、暗号化して設定ファイルに持たせることができる。

```powershell
$ ballerina encrypt
# Enter value:
# 
# Enter secret:
# 
# Re-enter secret to verify:
# 
# Add the following to the runtime config:
# @encrypted:{ufeieFEIUFE309f83kJIFJjeifeKFE=}
# 
# Or add to the runtime command line:
# -e<param>=@encrypted:{ufeieFEIUFE309f83kJIFJjeifeKFE=}# 
```

これを設定ファイルの値にそのまま入れる。

```toml:myconfig.toml
access_token="@encrypted:{eDLMHga9NW15siGHplUzlPP+9kALRMDA1f1rPBEJXJc=}"
```

```powershell
$ ballerina run -c .\myconfig.toml .\hello_service.bal
# ballerina: enter secret for config value decryption:
# 
# ↑ ここで、`Enter secret:` で入力したパスワードを入力
# ↓ 成功すると、サーバが動き出す
# 
# Initiating service(s) in 'hello_service.bal'
# [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9090
```

```powershell
$ curl http://localhost:9090/api/say -X POST
# encrypted value
```

## Parallel, Asynchronous

### ● Parallel

ballerina は Worker と呼ばれる単位で並列化されている。ダイアグラムの Actor に相当するのがこの Worker だ。

https://ballerina.io/learn/by-example/workers.html
https://ballerina.io/learn/by-example/worker-interaction.html

```php
import ballerina/io;
import ballerina/runtime;
public function main() {
    worker w1 {
        int i = 100;
        float k = 2.34;
        io:println("[w1 -> w2] i: ", i, " k: ", k);

        (i, k) -> w2;

        json j = {};
        j = <- w2;
        string jStr = j.toString();
        io:println("[w1 <- w2] j: ", jStr);
        io:println("[w1 ->> w2] i: ", i);

        () send = i ->> w2;

        io:println("[w1 ->> w2] successful!!");

        io:println("[w1 -> w3] k: ", k);
        k -> w3;
        k -> w3;
        k -> w3;

        io:println("Waiting for worker w3 to fetch messages..");
        error? flushResult = flush w3;
        io:println("[w1 -> w3] Flushed!!");
    }

    worker w2 {
        int iw;
        float kw;
        (int, float) vW1 = (0, 1.0);
        vW1 = <- w1;
        (iw, kw) = vW1;
        io:println("[w2 <- w1] iw: " + iw + " kw: " + kw);

        json jw = { "name": "Ballerina" };
        io:println("[w2 -> w1] jw: ", jw);
        jw -> w1;

        int lw;
        runtime:sleep(5);
        lw = <- w1;
        io:println("[w2 <- w1] lw: " + lw);
    }

    worker w3 {
        float mw;
        runtime:sleep(50);
        mw = <- w1;
        mw = <- w1;
        mw = <- w1;
        io:println("[w3 <- w1] mw: ", mw);
    }

    wait w1;
}
```

メッセージ送信の場合、`<-` で待ち受けて、`->` でメッセージを送信。
`->>` は送信して受信を待つ。`() send = i ->> w2;` では i を w2 に送って、`()` が返るのを待つ = w2 が終わるのを待つ。

受信と送信では型検査がなされており、型安全となっている。
また、受信と送信の回数はチェックされていて、回数が合わないとコンパイルエラーとなる。
試しに `foreach` の中で送信してみたが、`invalid worker send statement position, must be a top level statement in a worker` と怒られた。

また、Fork-Join にネイティブに対応している。

```php
fork {
    worker w1 {
        int i = 23;
        string s = "Foo";
        io:println("[w1] i: ", i, " s: ", s);

        (i, s) -> fork;
    }

    worker w2 {
        float f = 10.344;
        io:println("[w2] f: ", f);
        f -> fork;
    }

} join (all) (map results) {
    int iW1;
}
```

### ● Asynchronous

標準エンドポイントの多くは同期的に処理されるが、Future 化する事もできる。

```php
future<http:Response | error> f1 = start nasdaqServiceEP -> get("/nasdaq/quote/GOOG");
io:println(" >> Invocation completed!" + " Proceed without blocking for a response.");

var response = await f1;
```

`start` で Future 化し、`await` で結果を待つ。

## API

Microservice 間でのデータの通信や、外部サービスとの通信を安全に行うための様々な配慮がなされている。

### ● Typesafe payload

サービス間ペイロードのデータ構造としては JSON と XML をデフォルトで扱える。
ballerina では、Union 型を使ったパーシングを行いうことで、型安全を保っている。

```php
type Payment {
    string name,
    string cardnumber,
    int month,
    int year,
    int cvc;
};

json payload = check request.getJsonPayload();
Payment|error p = <Payment>payload;
match p {
    Payment x => {
        io:println(x);
        res.statusCode = 200;
        // return the JSON that has been created
        res.setJsonPayload(check <json>x);
    }
    error e => {
        res.statusCode = 400 ;
        // return the error message if the JSON failed to parse
        res.setStringPayload(e.message);
    }
    _ = caller -> respond (res);
}
```

`<>` でキャストを行っているが、その際に成功したか失敗したかの Union 型が返ってくる。Result や Either の様なもの。

### ● gRPC

ballerina は、gRPC へもネイティブに対応している。

以下、`proto\order.proto` を実装するものとする。

```proto:proto\order.proto
syntax = "proto3";
package grpc_service;
import "google/protobuf/wrappers.proto";
service order {
    rpc findOrder(google.protobuf.StringValue) returns (google.protobuf.StringValue);
    rpc addOrder(orderInfo) returns (google.protobuf.StringValue);
    rpc updateOrder(orderInfo) returns (google.protobuf.StringValue);
    rpc cancelOrder(google.protobuf.StringValue) returns (google.protobuf.StringValue);
}
message orderInfo {
    string id = 1;
    string name = 2;
    string description = 3;
}
```

#### for Service

```powershell
$ ballerina grpc --input proto\order.proto --output grpc_service --mode service
# Downloading proc executor.
# Download successfully completed.
# Successfully extracted library files.
# Successfully generated ballerina file.

$ tree grpc_service 
# grpc_service
# ├── order_pb.bal
# └── order_sample_service.bal
```

サービス側を作成するときは `--mode service` をつける。`--output` が出力先。

`order_pb.bal` が Stub で、`order_sample_service.bal` が実装例。

```php:grpc_service\order_sample_service.bal
import ballerina/grpc;
listener grpc:Listener ep = new (9000);

service order on ep {
    resource function findOrder(grpc:Caller caller, string value) {
    }
    resource function addOrder(grpc:Caller caller, orderInfo value) {
    }
    resource function updateOrder(grpc:Caller caller, orderInfo value) {
    }
    resource function cancelOrder(grpc:Caller caller, string value) {
    }
}
```

例えば、以下のように実装していくことになる。

```php:grpc_service\order_sample_service.bal
...

service order on ep {
...
    resource function findOrder(grpc:Caller caller, string value) {
        orderInfo o = {id: value, name: string `name {{value}}`, description: "no description"};
        var jsonValue = json.convert(o);
        if (jsonValue is error) {
            _ = caller->sendError(grpc:INTERNAL, <string>jsonValue.detail().message);
        } else {
            json orderDetails = jsonValue;
            string payload = orderDetails.toString();
            _ = caller->send(payload);
            _ = caller->complete();
        }
    }
...
}
```

#### for Client

```powershell
$ ballerina grpc --input proto\order.proto --output grpc_client --mode client
# Successfully extracted library files.
# Successfully generated ballerina file.

$ tree grpc_client
# grpc_client
# ├── order_pb.bal
# └── order_sample_client.bal
```

```php:grpc_service\order_sample_client.bal
import ballerina/grpc;
import ballerina/io;

public function main (string... args) {
    orderClient ep = new("http://localhost:9090");
    orderBlockingClient blockingEp = new("http://localhost:9090");

}

service orderMessageListener = service {
    resource function onMessage(string message) {
        io:println("Response received from server: " + message);
    }
    resource function onError(error err) {
        io:println("Error from Connector: " + err.reason() + " - " + <string>err.detail().message);
    }
    resource function onComplete() {
        io:println("Server Complete Sending Responses.");
    }
};
```

クライアント側は、`main` にあるように Stub の `orderClient` や `orderBlockingClient` の Object を生成しながら実装していくことになるだろう。

### ● OpenAPI Specification

ballerina は、RESTful Endpoint からの OpenAPI Specification 生成、また OpenAPI Specification からのコード生成に対応している。
https://ballerina.io/learn/by-guide/open-api-based-service/

#### Ballerina → OpenAPI 

ballerina のコードから OpenAPI Specification を生成する。
https://ballerina.io/learn/by-example/ballerina-to-swagger.html

```php
import ballerina/http;
import ballerina/log;
listener http:Listener helloEp = new(9090);
service hello on helloEp {
    @http:ResourceConfig {
        methods: ["GET"],
        path: "/say"
    }
    resource function hi(http:Caller caller, http:Request request) {
        http:Response res = new;
        res.setPayload("Hello World!");
        var result = caller->respond(res);
        if (result is error) {
           log:printError("Error when responding", err = result);
        }
    }
}
```

```powershell
$ ballerina swagger export .\hello_service.bal
# successfully generated swagger definition for input file - .\hello_service.bal
```

これで、ルートフォルダに `hello_service.swagger.yaml` ができる。

Swagger UI で見てみると、

```
$ docker run -p 8080:8080 -v "${pwd}\hello_service.swagger.yaml:/swagger.yaml" -e SWAGGER_JSON=/swagger.yaml swaggerapi/swagger-ui
```
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/9fc12954-9b85-6c5b-9d6f-6cd39ecf1fd0.png" width="80%" />

#### OpenAPI → Ballerina

今度は、OpenAPI Specification から Ballerina の実装 ( Service Mock / Client ) のコードを生成する。
先程生成した定義を使う。

##### 1. Service Mock

`-m` で出力するモジュールを指定する。`-o` で出力先フォルダを指定することもできる。

```powershell
$ ballerina swagger mock .\hello_service.swagger.yaml -m swagger_mock
# successfully generated ballerina mock service for input file - .\hello_service.swagger.yaml

$ tree swagger_mock
# swagger_mock      
# ├── gen           
# │  ├── hello.bal  
# │  └── schema.bal 
# └── hello_impl.bal
```

```php:swagger_mock\gen\hello.bal
import ballerina/http;
import ballerina/log;
import ballerina/mime;
import ballerina/swagger;

listener http:Listener ep0 = new(9090);

@swagger:ServiceInfo {
    title: "hello",
    serviceVersion: "1.0.0"
}
@http:ServiceConfig {
    basePath: "/"
}
service hello on ep0 {
    @swagger:ResourceInfo {
        summary: ""
    }
    @http:ResourceConfig {
        methods:["GET"],
        path:"/say"
    }
    resource function hi (http:Caller outboundEp, http:Request _hiReq) {
        http:Response _hiRes = hi(_hiReq);
        _ = outboundEp->respond(_hiRes);
    }
}
```

```php:swagger_mock\hello_impl.bal
public function hi (http:Request _hiReq) returns http:Response {
    // stub code - fill as necessary
    http:Response _hiRes = new;
    string _hiPayload = "Sample hi Response";
    _hiRes.setTextPayload(_hiPayload);

	return _hiRes;
}
```

実装と API を分けるのは、gRPC と同じ考え方。

##### 2. Client

```powershell
$ ballerina swagger client .\hello_service.swagger.yaml -m swagger_client
successfully generated ballerina client for input file - .\hello_service.swagger.yaml

$ tree swagger_client
# swagger_mock      
# └── gen           
#    ├── hello.bal  
#    └── schema.bal 
```

```php:swagger_mock\gen\hello.bal
import ballerina/io;
import ballerina/mime;
import ballerina/http;

//=====================================
//============Client Config============
//=====================================
public type helloClientConfig record {
    string serviceUrl;
    http:ClientEndpointConfig clientConfig;
};

//=======================================
//============Client Endpoint============
//=======================================
public type helloClientEp object {
    public http:Client client;
    public helloClientConfig config;

    public function init(helloClientConfig config) {
        http:Client httpEp = new(config.serviceUrl, config = {auth: config.clientConfig.auth, cache: config.clientConfig.cache});
        self.client = httpEp;
        self.config = config;
    }

    public function getCallerActions() returns (helloClient) {
        return new helloClient(self);
    }
};

//==============================
//============Client============
//==============================
public type helloClient object {
    public helloClientEp clientEp;

    new (clientEp) {}

    public function hi() returns http:Response | error {
        http:Client _hiEp = self.clientEp.client;
        http:Request request = new;

        // TODO: Update the request as needed
        return check _hiEp->GET("/say", request = request);
    }
    
};
```

使う時は、接続情報を渡した `helloClientEp` を使って `helloClient` を初期化みたいな感じだろう。

## Deployment

[既に Dockerfile 生成や  については述べた](https://qiita.com/drafts/42e3f06949438f7d430d/edit#docker) が、その他に Kubernetes へのデプロイにも対応している。
https://ballerina.io/learn/how-to-deploy-and-run-ballerina-programs/

### ● Docker

改めてまとめると、

- Dockerfile generation
    - copy local files
- Docker image generation
- Docker push support with Docker registry
- Docker-based Ballerina debug support
- Copy file support

等をコード内のアノテーションで指定ができ、`ballerina build` 時に実行してくれる。

### ● Kubernetes

Kubernetes も Docker と同様、コード内のアノテーションを解釈し、Build 時に実行してくれる。

- Kubernetes deployment
- Kubernetes service
- Kubernetes liveness probe
- Kubernetes ingress
- Kubernetes horizontal pod autoscaler
- Docker image generation
- Docker push with remote Docker registry
- Kubernetes secret
- Kubernetes config map
- Kubernetes persistent volume claim

試しに、以下で Build してみる。( 公式サンプルでは Ingress があったが、面倒なので削除した )

```php:hello_service.bal
import ballerina/config;
import ballerina/http;
import ballerina/log;
import ballerina/mysql;
import ballerinax/kubernetes;


mysql:Client employeeDB = new ({
    host:config:getAsString("database.host"),
    port:3306,
    name:config:getAsString("database.name"),
    username:config:getAsString("database.username"),
    password:config:getAsString("database.password")
});

@kubernetes:Service {
    serviceType:"NodePort",
    name:"ballerina-guides-employee-database-service"
}
listener http:Listener helloEp = new (9090, config = {
    secureSocket:{
        keyStore:{
            path:"${ballerina.home}/bre/security/ballerinaKeystore.p12",
            password:config:getAsString("store.key.password")
        },
        trustStore:{
            path:"${ballerina.home}/bre/security/ballerinaTruststore.p12",
            password:config:getAsString("store.trust.password")
        }
    }
});


@kubernetes:ConfigMap {
    ballerinaConf:"./myconfig.toml"
}
@kubernetes:Deployment {
    image:"employee_search_service_image:v1.0",
    name:"employee_search_service",
    copyFiles:[{target:"/ballerina/runtime/bre/lib",source:"./resources/mysql-connector-java-8.0.11.jar"}]
}
service records on helloEp {
    @http:ResourceConfig {
        methods: ["GET"],
        path: "/one"
    }
    resource function getOne(http:Caller caller, http:Request request) {
        http:Response res = new;
        res.setPayload("One");
        var result = caller->respond(res);

        if (result is error) {
           log:printError("Error when responding", err = result);
        }
    }
}
```

設定ファイルは以下。

```toml:myconfig.toml
[database]
host = "mysql-server"
name = "EMPLOYEE_RECORDS"
username = "root"
password = "root"

[store.key]
password = "abc123"

[store.trust]
password = "xyz123"
```

また、今回は依存するファイルとして `mysql-connector-java-8.0.11.jar` も含めたいので、MySQL のサイトから取得し、`.\resources` 以下に配置している。

この状態で Build を行う。

```powershell
$ ballerina build
# Compiling source
#     hello_service.bal
#     user/math:0.0.1
# 
# Running tests
#     user/math:0.0.1
#         [pass] testFunction
# 
#         1 passing
#         0 failing
#         0 skipped
# 
# Generating executables
#     ./target/hello_service.balx
#         @kubernetes:Service                      - complete 1/1
#         @kubernetes:Ingress                      - complete 1/1
#         @kubernetes:Secret                       - complete 1/1
#         @kubernetes:ConfigMap                    - complete 1/1
#         @kubernetes:Deployment                   - complete 1/1
#         @kubernetes:Docker                       - complete 3/3
#         @kubernetes:Helm                         - complete 1/1
# 
#         Run the following command to deploy the Kubernetes artifacts:
#         kubectl apply -f C:\sample_1219\target\kubernetes\hello_service
# 
#         Run the following command to install the application using Helm:
#         helm install --name employee-search-service C:\sample_1219\target\kubernetes\hello_service\employee-search-service

$ tree target\kubernetes
# target/kubernetes
# └── hello_service
#    ├── docker
#    │  ├── Dockerfile
#    │  └── mysql-connector-java-8.0.13.jar
#    ├── employee-search-service
#    │  ├── Chart.yaml
#    │  └── templates
#    │     └── hello_service.yaml
#    └── hello_service.yaml
```

kubectl で直接扱うためのリソースが `target\kubernetes\hello_service\hello_service.yaml` で、Helm の Local Charts が `target\kubernetes\hello_service\employee-search-service` となる。

**Docker Image**

まずは Docker Images を確認すると、指定通りにイメージが作成されている。

```powershell
$ docker images
# REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
# employee_search_service_image              v1.0                8f9a7acc559d        13 minutes ago      131MB
# ...
```

`target\kubernetes\hello_service\docker\Dockerfile` の中身は Docker アノテーションの時とほぼ同じだ。

```dockerfile
# Auto Generated Dockerfile

FROM ballerina/ballerina-runtime:0.990.0
LABEL maintainer="dev@ballerina.io"

COPY hello_service.balx /home/ballerina 

COPY mysql-connector-java-8.0.13.jar /ballerina/runtime/bre/lib
EXPOSE  9090
CMD ballerina run --config ${CONFIG_FILE} hello_service.balx
```

**Kubernetes resources**

次に、YAML ファイルを見ていく。
大体指定したまま作成されている。

```yaml
---
apiVersion: "v1"
kind: "Service"
metadata:
  annotations: {}
  finalizers: []
  labels:
    app: "hello_service"
  name: "ballerina-guides-employee-database-service"
  ownerReferences: []
spec:
  externalIPs: []
  loadBalancerSourceRanges: []
  ports:
  - port: 9090
    protocol: "TCP"
    targetPort: 9090
  selector:
    app: "hello_service"
  type: "NodePort"
---
apiVersion: "extensions/v1beta1"
kind: "Ingress"
metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    kubernetes.io/ingress.class: "nginx"
  finalizers: []
  labels:
    app: "hello_service"
  name: "ballerina-guides-employee-database-service"
  ownerReferences: []
spec:
  rules:
  - host: "ballerina.guides.io"
    http:
      paths:
      - backend:
          serviceName: "ballerina-guides-employee-database-service"
          servicePort: 9090
        path: "/"
  tls:
  - hosts:
    - "ballerina.guides.io"
---
apiVersion: "v1"
kind: "Secret"
metadata:
  annotations: {}
  finalizers: []
  labels: {}
  name: "helloep-secure-socket"
  ownerReferences: []
data: ballerinaTruststore.p12: "MIMBuTc ................<省略>"
stringData: {}
---
apiVersion: "v1"
kind: "ConfigMap"
metadata:
  annotations: {}
  finalizers: []
  labels: {}
  name: "records-ballerina-conf-config-map"
  ownerReferences: []
data:
  ballerina.conf: "[database]\nhost = \"mysql-server\"\nname = \"EMPLOYEE_RECORDS\"\
    \nusername = \"root\"\npassword = \"root\"\n\n[store.key]\npassword = \"abc123\"\
    \n\n[store.trust]\npassword = \"xyz123\"\n"
---
apiVersion: "extensions/v1beta1"
kind: "Deployment"
metadata:
  annotations: {}
  finalizers: []
  labels:
    app: "hello_service"
  name: "employee-search-service"
  ownerReferences: []
spec:
  replicas: 1
  template:
    metadata:
      annotations: {}
      finalizers: []
      labels:
        app: "hello_service"
      ownerReferences: []
    spec:
      containers:
      - args: []
        command: []
        env:
        - name: "CONFIG_FILE"
          value: "/home/ballerina/conf/ballerina.conf"
        envFrom: []
        image: "employee_search_service_image:v1.0"
        imagePullPolicy: "IfNotPresent"
        name: "employee-search-service"
        ports:
        - containerPort: 9090
          protocol: "TCP"
        volumeMounts:
        - mountPath: "\\ballerina\\runtime\\bre\\security"
          name: "helloep-secure-socket-volume"
          readOnly: true
        - mountPath: "/home/ballerina/conf/"
          name: "records-ballerina-conf-config-map-volume"
          readOnly: false
      hostAliases: []
      imagePullSecrets: []
      initContainers: []
      nodeSelector: {}
      tolerations: []
      volumes:
      - name: "helloep-secure-socket-volume"
        secret:
          items: []
          secretName: "helloep-secure-socket"
      - configMap:
          items: []
          name: "records-ballerina-conf-config-map"
        name: "records-ballerina-conf-config-map-volume"
```

Deploy してみる。

```powershell
$ helm install --name employee-search-service .\target\kubernetes\hello_service\employee-search-service

$ kubectl get pod
# NAME                                       READY     STATUS    RESTARTS   AGE
# employee-search-service-5f647b9cd6-pxmmx   0/1       Error     1          48s
```

エラーで動かない。原因究明中。
しかし、Kubernetes への対応がどの様なものかは大体理解できた。

## Observable

ballerina は、メトリクス監視、分散トラッキング、ログ管理機能を標準で持っている。
https://ballerina.io/learn/how-to-observe-ballerina-code/

以降は、以下のサービスを監視することとする。

```php:hello_service.bal
import ballerina/http;
import ballerina/log;

service hello on new http:Listener(9090) {

    resource function sayHello (http:Caller caller, http:Request req) {
        log:printInfo("This is a test Info log");
        log:printError("This is a test Error log");
        log:printWarn("This is a test Warn log");
        http:Response res = new;
        res.setPayload("Hello, World!");
        _ = caller -> respond(res);
    }
}
```

### ● メトリクス監視

メトリクス監視には、Prometheus を想定している。

```powershell
$ ballerina run --observe .\hello_service.bal
# ballerina: started publishing tracers to Jaeger on localhost:5775
# Initiating service(s) in 'C:\PROGRA~1\BALLER~1\BALLER~1.0\bin\..\lib\balx\prometheus\reporter.balx'
# [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9797
# ballerina: started Prometheus HTTP endpoint 0.0.0.0:9797
# Initiating service(s) in 'hello_service.bal'
# [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9090
```

`--observe` を付けて起動すると、メトリクス監視専用のエンドポイント ( デフォルトで Port 9797 ) を追加し、`/metrics` でメトリクスが公開される。

また、設定ファイルで動作をカスタムすることもできる。

```toml:myconfig.toml
[b7a.observability.metrics]
enabled=true
reporter="prometheus"

[b7a.observability.metrics.prometheus]
port=9797
host="0.0.0.0"
```


今回のコードで公開されるメトリクスは以下である。

```powershell
$ curl http://localhost:9797/metrics
# TYPE ballerina_scheduler_waiting_for_lock_worker_count_value gauge
ballerina_scheduler_waiting_for_lock_worker_count_value 0.0
# HELP ballerina_http:Caller_requests_total_value Total number of requests
# TYPE ballerina_http:Caller_requests_total_value counter
ballerina_http:Caller_requests_total_value{action="respond",http_status_code="200",} 1.0
# HELP http_inprogress_requests_value Inprogress Requests
# TYPE http_inprogress_requests_value gauge
http_inprogress_requests_value{resource="getMetrics",service="PrometheusReporter$$service$0",} 1.0
# HELP ballerina_http:Caller_response_time_seconds_value Response Time
# TYPE ballerina_http:Caller_response_time_seconds_value gauge
ballerina_http:Caller_response_time_seconds_value{action="respond",http_status_code="200",} 0.1376805
# HELP ballerina_http:Caller_response_time_seconds A Summary of ballerina_http:Caller_response_time_seconds for window of 60000
# TYPE ballerina_http:Caller_response_time_seconds summary
ballerina_http:Caller_response_time_seconds_mean{action="respond",http_status_code="200",timeWindow="60000",} 0.0
ballerina_http:Caller_response_time_seconds_max{action="respond",http_status_code="200",timeWindow="60000",} 0.0
ballerina_http:Caller_response_time_seconds_min{action="respond",http_status_code="200",timeWindow="60000",} 0.0
ballerina_http:Caller_response_time_seconds_stdDev{action="respond",http_status_code="200",timeWindow="60000",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="60000",quantile="0.33",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="60000",quantile="0.5",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="60000",quantile="0.66",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="60000",quantile="0.99",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="60000",quantile="0.999",} 0.0
# HELP ballerina_http:Caller_response_time_seconds A Summary of ballerina_http:Caller_response_time_seconds for window of 300000
# TYPE ballerina_http:Caller_response_time_seconds summary
ballerina_http:Caller_response_time_seconds_mean{action="respond",http_status_code="200",timeWindow="300000",} 0.13671875
ballerina_http:Caller_response_time_seconds_max{action="respond",http_status_code="200",timeWindow="300000",} 0.13671875
ballerina_http:Caller_response_time_seconds_min{action="respond",http_status_code="200",timeWindow="300000",} 0.13671875
ballerina_http:Caller_response_time_seconds_stdDev{action="respond",http_status_code="200",timeWindow="300000",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="300000",quantile="0.33",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="300000",quantile="0.5",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="300000",quantile="0.66",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="300000",quantile="0.99",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="300000",quantile="0.999",} 0.13671875
# HELP ballerina_http:Caller_response_time_seconds A Summary of ballerina_http:Caller_response_time_seconds for window of 900000
# TYPE ballerina_http:Caller_response_time_seconds summary
ballerina_http:Caller_response_time_seconds_mean{action="respond",http_status_code="200",timeWindow="900000",} 0.13671875
ballerina_http:Caller_response_time_seconds_max{action="respond",http_status_code="200",timeWindow="900000",} 0.13671875
ballerina_http:Caller_response_time_seconds_min{action="respond",http_status_code="200",timeWindow="900000",} 0.13671875
ballerina_http:Caller_response_time_seconds_stdDev{action="respond",http_status_code="200",timeWindow="900000",} 0.0
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="900000",quantile="0.33",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="900000",quantile="0.5",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="900000",quantile="0.66",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="900000",quantile="0.99",} 0.13671875
ballerina_http:Caller_response_time_seconds{action="respond",http_status_code="200",timeWindow="900000",quantile="0.999",} 0.13671875
# HELP ballerina_http:Caller_3XX_requests_total_value Total number of requests that resulted in HTTP 1xx informational responses
# TYPE ballerina_http:Caller_3XX_requests_total_value counter
ballerina_http:Caller_3XX_requests_total_value{action="respond",} 0.0
# HELP ballerina_http:Caller_inprogress_requests_value Inprogress Requests
# TYPE ballerina_http:Caller_inprogress_requests_value gauge
ballerina_http:Caller_inprogress_requests_value{action="respond",} 0.0
# HELP ballerina_http:Caller_2XX_requests_total_value Total number of requests that resulted in HTTP 1xx informational responses
# TYPE ballerina_http:Caller_2XX_requests_total_value counter
ballerina_http:Caller_2XX_requests_total_value{action="respond",} 1.0
# HELP http_requests_total_value Total number of requests
# TYPE http_requests_total_value counter
http_requests_total_value{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",} 1.0
# TYPE ballerina_scheduler_running_worker_count_value gauge
ballerina_scheduler_running_worker_count_value 0.0
# HELP ballerina_http:Caller_5XX_requests_total_value Total number of requests that resulted in HTTP 1xx informational responses
# TYPE ballerina_http:Caller_5XX_requests_total_value counter
ballerina_http:Caller_5XX_requests_total_value{action="respond",} 0.0
# HELP ballerina_http:Caller_1XX_requests_total_value Total number of requests that resulted in HTTP 1xx informational responses
# TYPE ballerina_http:Caller_1XX_requests_total_value counter
ballerina_http:Caller_1XX_requests_total_value{action="respond",} 0.0
# TYPE ballerina_scheduler_excepted_worker_count_value gauge
ballerina_scheduler_excepted_worker_count_value 0.0
# TYPE ballerina_scheduler_ready_worker_count_value gauge
ballerina_scheduler_ready_worker_count_value 0.0
# TYPE ballerina_scheduler_waiting_for_response_worker_count_value gauge
ballerina_scheduler_waiting_for_response_worker_count_value 0.0
# HELP startup_time_milliseconds_value Startup time in milliseconds
# TYPE startup_time_milliseconds_value gauge
startup_time_milliseconds_value 19697.0
# HELP http_response_time_seconds_value Response Time
# TYPE http_response_time_seconds_value gauge
http_response_time_seconds_value{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",} 0.4045906
# HELP http_response_time_seconds A Summary of http_response_time_seconds for window of 60000
# TYPE http_response_time_seconds summary
http_response_time_seconds_mean{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",} 0.0
http_response_time_seconds_max{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",} 0.0
http_response_time_seconds_min{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",} 0.0
http_response_time_seconds_stdDev{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",quantile="0.33",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",quantile="0.5",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",quantile="0.66",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",quantile="0.99",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="60000",quantile="0.999",} 0.0
# HELP http_response_time_seconds A Summary of http_response_time_seconds for window of 300000
# TYPE http_response_time_seconds summary
http_response_time_seconds_mean{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",} 0.404296875
http_response_time_seconds_max{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",} 0.404296875
http_response_time_seconds_min{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",} 0.404296875
http_response_time_seconds_stdDev{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",quantile="0.33",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",quantile="0.5",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",quantile="0.66",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",quantile="0.99",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="300000",quantile="0.999",} 0.404296875
# HELP http_response_time_seconds A Summary of http_response_time_seconds for window of 900000
# TYPE http_response_time_seconds summary
http_response_time_seconds_mean{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",} 0.404296875
http_response_time_seconds_max{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",} 0.404296875
http_response_time_seconds_min{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",} 0.404296875
http_response_time_seconds_stdDev{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",} 0.0
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",quantile="0.33",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",quantile="0.5",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",quantile="0.66",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",quantile="0.99",} 0.404296875
http_response_time_seconds{protocol="http",http_url="/metrics",resource="getMetrics",http_method="GET",service="PrometheusReporter$$service$0",timeWindow="900000",quantile="0.999",} 0.404296875
# TYPE ballerina_scheduler_paused_worker_count_value gauge
ballerina_scheduler_paused_worker_count_value 0.0
# HELP ballerina_http:Caller_4XX_requests_total_value Total number of requests that resulted in HTTP 1xx informational responses
# TYPE ballerina_http:Caller_4XX_requests_total_value counter
ballerina_http:Caller_4XX_requests_total_value{action="respond",} 0.0
```

今回は HTTP エンドポイントのみだが、多分他のプロトコルのエンドポイントを増やせばまた違ったメトリクスが取れるのかも知れない。
後は Prometheus の収集対象に追加するだけで、メトリクス監視が可能となる。

独自メトリクスを追加するのも簡単だ。

https://ballerina.io/learn/by-example/counter-metrics.html
https://ballerina.io/learn/by-example/gauge-metrics.html

### ● 分散トレーシング

分散トレーシングは OpenTracing  に対応しているので、OpenTracing  準拠の [Jaeger](https://www.jaegertracing.io/) や [Zipkin](https://zipkin.io/) などを利用できる。

まずは Jaeger を立ち上げる。

```powershell
$ docker run -d -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp -p5778:5778 -p16686:16686 -p14268:14268 jaegertracing/all-in-one:latest
```

有効にするには、メトリクスと同様 `--observe` を付けて起動する。
接続女王法などは設定ファイルで設定する。デフォルトで対応しているのは、`Jaeger` と `Zipkin` だ。

```toml:myconfig.toml
[b7a.observability.tracing]
enabled=true
name="jaeger"

[b7a.observability.tracing.jaeger]
reporter.hostname="localhost"
reporter.port=5775
sampler.type="const"
sampler.param=1.0
reporter.flush.interval.ms=2000
reporter.max.buffer.spans=1000
```

この状態で立ち上げ、Jaeger の画面を確認すれば、トレースができていることが確認できる。

![2018-12-20_21h40_45.png](https://qiita-image-store.s3.amazonaws.com/0/20198/f960eaad-8bf2-3f28-5060-3c14cf8af5bf.png)

スパンを操作するモジュールは、以下を参考に。
https://ballerina.io/learn/by-example/tracing.html

### ● ログ集約

ballerina では、ログの集約 ~ 可視化 プラットフォームとして Elastic Stack ( ELK + Beats ) をサポートしている。

ただ、サポートとは、**標準の Logger で stdout にログ吐き出せるよ** というだけのもので、それをファイルにリダイレクトしつつ Filebeats で監視して Logstash へと送るというやり方。

正直、サポートと謳っているのだから、てっきり beats 機能を標準的に持っているか、もしくは Elasticsearch に直接送信するのでは、と期待していた。

ログ収集には標準仕様がないので、Docker や Kubernetes への対応も考えると『stdout に出しておけばいいや』になるんだろう。
そのおかげで Fluentd での代用も可能だ。

# サンプル体験

サンプルが多い！！
https://ballerina.io/learn/by-example/

ガイドも多い！！！！
https://ballerina.io/learn/by-guide/

もはや力尽きているので、これらはまた次の機会に。
ガイドを見ると、面白そうな Topics がいっぱいあるので、後でまた読みたい。

# 感想

まず思いついた感想が、**とんでもないものに手を出してしまった** でした。
言語仕様もマニュアルも膨大で、そして今も肥大化し続けている。BLAME! の超構造体状態。

DevOps を極めるとこうなるのか、という感想。しかし、これを使いこなせる Fullstack なソフトウェアエンジニアはどれほど居るんだろう。

# 参考

https://thenewstack.io/ballerina-a-programming-language-for-cloud-native-computing/

# あとがき

※ この記事は個人の見解であり、所属する組織を代表するものではありません。