読者です 読者をやめる 読者になる 読者になる

/home/by-natures/dev*

ソフトウェア開発者として働く人の技術的なメモ

Sensu の Slack ハンドラのテンプレート記述方法

Sensu に、通知を Slack に送るハンドラがあるのですが、通知内容を整形する方法を調査しました。

github.com

公式 GitHub にも簡単に紹介されているのですが、サンプルが見当たらなかったのでご紹介します:

/etc/sensu/conf.d/slack.json

例として slack.json というファイルを用意しました。

Sensu は conf.d/ 配下の .json ファイルをかき集めるので、slack.json という形でなくても構いません。 handlers.json の中に書くのが普通でしょうか。

{
  "slack": {
    "webhook_url": "<WebHook URL>",
    "payload_template" : "/etc/sensu/conf.d/slack_payload_template.erb"
  }
}
/etc/sensu/conf.d/slack_payload_template.erb

テンプレートは ERB 形式です。conf.d/ 配下でなくてもよいので、適当な場所に保存します。

{
  "username": "<ユーザー名>",
  "channel": "<チャネル名>",
  "attachments": [
    {
      "fallback": "<%= @event["check"]["output"] %>",
      "color": "<%= color %>",
      "title": "<%= @event["check"]["name"] %> (<%= @event["client"]["name"] %>)",
      "text": "<%= @event["check"]["output"].gsub('"', '\\"') %>"
    }
  ]
}

テンプレートを利用しない場合は、slack.jsonusername, channel を入れるのですが、テンプレートを利用する場合は channelだけはテンプレート側に書かないと動きません。(処理は このあたり

JMockit で File クラスをモックする

業務では JMockit(公式ページ)を利用してテストを書いているのですが、ファイル入出力を利用したクラスの単体テストが書きたいと思い、JMockit で File クラスをモックできないかを調べました。

JMockit とは

Java の単体テスト向けフレームワークの1つです。

似た名前のテストフレームワークに mockito というものがあります。同じようなことができるのですが、記述方法がかなり違います。また、Spring では mockito に依存しているプロジェクトがあるため、なんとなくで使っていると、JMockit を使っているつもりが、mockito だった…ということが起こりかねません。(JMockit のパッケージ名が mockit なのも混乱の元ではないでしょうか。。)

会話では JMockit は(ジェーモックイット)、mockito は(モキート)と言えば伝わりやすいかと思います。

違いがまとまった Qiita のページがありました:

qiita.com

File クラスをモックする

ファイル入出力の動作確認であれば、一時ファイルを使ってテストが終わったら破棄するようにしてもよいのですが、ファイルサイズによって分岐する処理をテストしたかったため、File クラスをモックできないかと調べました。

GoogleGroups で、JMockit での File クラスのモックについて議論がされています。

https://groups.google.com/forum/#%21topic/jmockit-users/ocPNN4RxlXk

その中の一つに次の方法があります:

  @Test
  public void testSomeMethod() throws Exception {
    // prepare
    new MockUp<File>() {
      @Mock public long length() { return 100; }  // 100byte
      @Mock boolean renameTo(File file) { return true; }
      @Mock boolean exists() { return true; }
    };

    // execute
    // Do execute some code

    // verify
    new Verifications() {{
      ...
    }};
  }

これを実行すると、File クラス全てがモックアップされます。動作としては分かりやすいのですが、File クラスのように様々なところで使われているクラスだと意図しないオブジェクトまでモックにしてしまい、NoClassDefFoundErrorClassNotFoundException が頻発するので使い方には注意が必要です。

mockito だと特定のオブジェクトだけモックにすることが簡単なようなのですが、まだ確認できていません。

HiveServer2 でジョブ進捗は取得できない?

HiveServer2 を使っているのですが、Hive クエリ(というか MapReduce)は場合によっては時間がかかるため、ユーザーの方に「クエリの進捗を知る方法はないか」と尋ねられました。利用側からすると、数十分で終わるのか、数時間で終わるのか、なんとなく具合を知りたいというのは最もな要望です。

Hive CLI(コマンドライン)から叩くと MapReduce のログがコンソールに出力されるので、ステージがどのぐらい進んだか、Map タスクがどのぐらい進んでいるかなど、進捗の具合を知ることができます。最後のタスクが非常に時間がかかることもあるので正確な目安にもならないのですが、何も出ないよりマシ…といったところでしょうか。

ただ、このような情報を HiveServer2 を利用して取得する方法はないようです:

qnalist.com

HiveServer2 は Thrift 上で構築されており、Thrift に定義されていなければ HiveServer2 ではジョブ進捗は取得できないとあります。Thrift は詳しくないのですが、以下のリポジトリ(ミラー)に Thrift インタフェースの定義があります。その中にはジョブの進捗を返すようなものはなく、単にジョブのステータス(終わったかどうかなど)を返すものがあるだけでした:

hive/TCLIService.thrift at 0af6cb42725659740a022044c6cc464ef1cf4e6b · apache/hive · GitHub

そもそも「ジョブ進捗」が何なのかは実行エンジンに依存するので、そんなものを返す API は存在しないのは当たり前かもしれません。ただHiveServer2 のログ自体には MapReduce の進捗が出ているので、これを補足して…という回答もありますが、かなり怪しい実装になりそうなので結局何もできなそうです。。同じような状況の方がいて何か対策されていたら教えて下さい。

Hive の予約語で CREATE TABLE, SELECT する

MySQL など通常の RDBMS もそうですが、Hive にも予約語があり、そのままではカラム名に利用することができません。バッククオート(バックティック)を利用することで予約後をテーブルのカラムに利用することができます。

Hadoop Blog: How, in hive, to create a column name that is same as a reserved keyword used by Hive

上のブログは CREATE TABLE について書かれていますが、SELECT 文でも同様にバッククオートでくくらないといけないようです。

HiveServer2 を JDBC で利用する場合に cancel() できるのは 0.13.0 から

調べたことのメモです。

HiveServer2 を JDBC から使ってるのですが、cancel() メソッドを使えるのは Hive 0.13.0 以降とのこと。2014年3月に以下のチケットが解決されているので、Hive を更新できていない環境ではクエリのキャンセルが行えない可能性がありますね。

[HIVE-6472] JDBC cancel will not work with current HiveServer2 - ASF JIRA

SpringFramework で OutputStream を扱う

大きなファイルを API を通じて返したい処理があり、そのままだとヒープ領域を圧迫して OOM エラーとなってしまうため、ストリーム処理で逐次データをクライアントへ返却するようにしました。

調べている最中に、まさに質問したかったことを Stackoverflow で発見:

stackoverflow.com

ServletResponse の output stream に書き込めば、一定サイズで flush してくれるので、使う側としては output stream にデータを流し込めさえすれば大丈夫でした。Spring Framework での処理の大枠としては以下の通りです。この API を叩くと、octet stream としてデータを返却するため、ブラウザは処理方法が分からずに受け取ったデータをダウンロードする挙動となります。

@RequestMapping(
  value = "/data",
  method = RequestMethod.GET,
  produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
)
public void getData(HttpServletResponse response) throws IOException {

  response.addHeader("Content-Disposition", "attachment; filename=\"data.txt\"");

  try (ServletOutputStream stream = response.getOutputStream()) {
    // stream にデータを書き込む
  }
}

動作確認に少し手間取りましたが、実装自体は簡単でした。これを調べている中で octet stream の挙動についても学べましたし、web はまだまだ知らないことが多いです。

ヒープダンプやプロファイルを見るのに jvisualvm が便利

以前の記事で JMX へ接続するためのクライアントとして、jconsole を紹介しました:

dev.bynatures.net

この後作業していて、jvisualvm というコマンドでもヒープダンプを見ることができると知り、そちらの方が便利だったので紹介します。

詳しい紹介記事

www.javaworld.com

少し古い記事ですが分かりやすかったです。以下のことが書かれていました:

Fortunately, two of the main features JMX developers were likely to use in JConsole can be used with VisualVM as well.

基本的には上位互換のツールのようです。上記記事では JConsole の MBean の機能を、プラグインを追加することで対応できるとあります。JMX に特化した JConsole とは違って汎用的な作りになっているようです。

jvisualvm は JDK 6 Update 7 から JDK 標準で付属します。開発は jvisualvm の方が後で、より高機能のようです。CPU, メモリのプロファイリングなども行え、どのメソッドが何回呼ばれているかなども確認することができます。

使い方

起動は jconsole と同様にコマンドライン上で、引数なしで叩きます:

$ jvisualvm

すると、GUI アプリケーションが立ち上がります:

f:id:bynatures:20160922023018p:plain

適当なアプリケーションに繋いでみました。jconsole よりカラフルで見やすいですね。タブで表示されるので、複数アプリケーションまとめて見ることもできます。

f:id:bynatures:20160922023856p:plain

以下の画面は、サンプラによってどのオブジェクトがどれだけ生成されているかを確認しているところです。

f:id:bynatures:20160922023939p:plain

JConsole でも可能ですが、jvisualvm でもその場で GC 実行・ヒープダンプの取得が行えます。