Go言語の学習がてら Healthcheck Notifier を作ってみた

f:id:t1000leaf:20180702012513p:plain

以前から、Go言語(golang)をちょっとやってみたりしてたんですが、実用的なものを作ってみたいと思い、 Healthcheck Notifier というものを作ってみました。
一つのソースファイルでできているとてもシンプルなものです。

Healthcheck Notifier

Webアプリケーションに対し、ヘルスチェックのリクエストを投げ、ステータスコード200以外が返答された場合、Alert を HipChat やメールで通知します。

クイックスタート

リリースページからバイナリをダウンロードします。
Windows, macOS, Linux のバイナリがあります。

ダウンロードしたバイナリを以下のように実行するだけで起動します(Linuxの例)。

./healthcheck-notifier-linux-amd64

Webアプリとして起動するので、ヘルスチェックの状態を Webブラウザから確認できます。
以下のURLをWebブラウザで開いてみてください。
http://localhost:18888/

以下は、スクリーンショットです。

f:id:t1000leaf:20180702012523p:plain

仕様

  • 監視対象のアプリケーションがヘルスチェックエラーになるとHipChatの設定された Room に通知し、設定されたメールアドレスにメール送信します。連続してエラーになる場合、通知はしません。
  • ヘルスチェックエラー後、ヘルスチェック成功を確認するとHipChatの設定された Room に通知し、設定されたメールアドレスにメール送信します。

設定ファイル

Healthcheck Notifier は、起動時に設定ファイルが存在しない場合、自分自身のアプリケーションを監視するような設定ファイルを例として作成します。この設定ファイルを書き換えます。
起動したディレクトリに以下のようなconfig.jsonが作成されていると思います。

{
    "cron": "*/10 * * * * *",
    "port": 18888,
    "hipchat-proxy": "",
    "hipchat-subdomain": "hipchat-subdomain",
    "smtp-server": "localhost:25",
    "mail-address-from": "from@example.com",
    "apps": [
        {
            "name": "self",
            "url": "http://localhost:18888/",
            "proxy": "",
            "hipchat-room": "1234567",
            "hipchat-token": "",
            "mail-address-to-down": ["to1@example.com", "to2@example.com"],
            "mail-address-to-up": ["to1@example.com", "to2@example.com"]
        }
    ]
}

設定内容については以下です。

プロパティ 説明
cron ヘルスチェックの周期(クーロン形式)
port モニター用のWeb画面が仕様するポート番号
hipchat-proxy HipChat通知で使用するプロキシ
hipchat-subdomain 利用しているHipChatのサブドメイン
stmp-server メール通知で使用するSMTPサーバ
mail-address-from メール通知するときのFROMアドレス
apps.name 監視対象のアプリケーション名称
apps.url 監視対象のURL
apps.proxy ヘルスチェックのリクエストで使用するプロキシ
apps.hipchat-room 通知する HipChat Room の ID
apps.hipchat-token HipChat のトーク
apps.mail-address-to-down ヘルスチェックエラーになった場合に送るメールアドレス
apps[].mail-address-to-up ヘルスチェックエラー状態からヘルスチェック成功した場合に送るメールアドレス

golang について

golang について覚えたことや感じたことを整理してみます。

最近の言語にある色々なものをバッサリ削った感じで、 プログラミング言語に理想や夢を求める人にはあまり受け入れられない現実指向な言語かと思います。
初めて学ぶプログラミング言語として、 golang というのはアリかもしれないと思った。

標準ライブラリが充実している

今回作成したソースファイルは一つだけですが、以下の事を行っています。

  • HTTPクライアント
  • HTTPサーバ
  • JSONパース
  • 並行処理
  • ファイル出力
  • ファイル入力
  • タスクスケジューリング

タスクスケジュール以外は、標準ライブラリでできてしまうところが凄い。
Javaだとサードパーティーのライブラリに頼っていたところが標準でできる感じです。

型宣言は、後置

まー最近の言語は型推論とかあるので型宣言は後置の方が自然ですよね。他の言語にあるようなコロンは不要。

var httpServer http.Server

型推論

ローカル変数の型推論。初回代入はコロンが必要

port := os.Getenv("PORT")

再代入は、コロンが不要。

port = "18888"

メソッド(メンバ関数)みたいなもの

以下のような感じで関数名の前に構造体のポインタを宣言することでメソッドように使えるようになる。

func (a *App) healthcheck() {
    // ...
}

複数行文字列リテラル

ヒアドキュメントというか複数行文字列リテラル
対象部分をバッククォートで括る。

`
{
    "cron": "*/10 * * * * *",
    "port": 18888
}
`

戻り値を複数返せる

送り側

func foo() (int, string) {
    return 1, "bar"
}

受け取り側

num, str := foo()

アンスコで戻り値の一部を無視

_, err := os.Stat(filename)

JSONパース

JSONに対応する構造体を作る。

type App struct {
    Name string   `json:"name"`
    URL  string   `json:"url"`
}

json:"XXX"JSONのパース処理への注釈のようなものになっているらしい。
変数NameJSONプロパティのnameと対応するようになる。Javaだとアノテーションでやっているような事だと思う。

import (json "encoding/json")
json.Unmarshal(バイト配列, 格納する構造体)

スコープ管理?

大文字で始まる関数・変数は、public(export)扱いになって、小文字で始まると private 的な扱いになる。
この命名規則による仕様は、正直ビミョーだなーと思うところ。。

Goroutine

golang の一番の特徴かもしれない goroutine
並行処理が軽量スレッドで簡単に実行できます。 以下が例です。

var wg sync.WaitGroup
for i := 0; i < len(hn.Apps); i++ {
    go func(i int) {
        wg.Add(1)
        hn.Apps[i].healthcheck()
        wg.Done()
    }(i)
}
wg.Wait()

go を付けた部分が並行で処理されます。
wg.Wait() ですべての処理が終了するのを待っています。

ロスコンパイル

これも golang の一番の特徴かもしれない。
WindowsmacOS, Linux のバイナリをコンパイルしたりできます。

Windows

$ GOOS=windows GOARCH=amd64 go build

Linux

$ GOOS=linux GOARCH=amd64 go build

macOS

$ GOOS=darwin GOARCH=amd64 go build

シングルバイナリで実行でき、ポータビリティが高いのが特徴だと思った。

サードパーティーのライブラリ

JSでいう npm , Java でいう Maven レポジトリみたいなものは存在せず、github とかのソースを直接引っ張ってくるみたい。
以下のような感じで import する。

import ("github.com/robfig/cron")

github から直接もってくると、そのライブラリの信頼性はどうなんだろうと少し不安になる。

ライブラリの依存管理

dep というコマンドをインストールして管理する。

dep をインストール

$ go get -u github.com/golang/dep/cmd/dep

設定ファイル(Gopkg.toml, Gopkg.lock)を生成

$ dep init

依存をvendorディレクトリ配下にインストール

$ dep ensure

今まで、色んなツールがあったみたいですが、dep が標準になるっぽい。

エラーハンドリング

golang には他の言語にあるような Exception がありません。
グローバルにエラーをハンドリングできず、細かくちゃんとエラーハンドリングしないとちょっとしたバグでプロセスが落ちてしまいそうです。

他の言語だと警告レベルのものがコンパイルエラーになって後回しにできない。未使用変数とか。

ないもの

GitHub について

今回、初めてリリースページを作ってみてコンパイル済みのバイナリをホスティングしてみた。

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

みんなのGo言語 現場で使える実践テクニック [ 松木雅幸 ]
価格:2138円(税込、送料無料) (2017/11/18時点)