tokuhirom's blog

ansistrano について

https://github.com/ansistrano/deploy/

Ansible 上で capistrano 的にデプロイできるようにするというプロジェクトがある。 これは一見良さそうだが、設定を真面目にやっていくと、同等のことを自分で手で書くのと同程度に大量の設定を書くことになるのが気になる。

Created: 2016-04-28 04:45:52
Updated: 2016-04-28 04:45:52

Alfred のワークフローを Perl で書きたい

https://github.com/tokuhirom/p5-Alfred

なんか Alfred2 を久々に使い始めている。

最近は IDE で生活する時間が長いので、terminal を開いてそこから peco を開いてみたいなことをするよりは、Alfred2 からサーバー選んで、そこから open してターミナル開いたほうがいいなぁと思ってみたという感じです。

で、perl で workflow を書いたらいいんだけど、perl 用のライブラリがなかったので書いた。

要するに、定義にしたがって、XML を吐くスクリプトがありゃいいんで、吐くようにしたってだけ。

ご利用ください。

これを使って、社内のデプロイツールからサーバーリスト撮ってきて、ターミナル開く機能と、サーバーリストからサーバーグループをいっきに csshX で開くってなツールを書きました。

あと、kurado を開くやつを作ろうと思ったけど、kurado には service list を取得する API がないのであきらめた。

Created: 2016-04-08 20:01:52
Updated: 2016-04-08 20:01:52

rxjava で peek() みたいなことしたい

java8 stream api でいうところの peek をするには doOnNext を利用すればよい。

http://stackoverflow.com/questions/30760768/rxjava-performing-a-peek-or-void-operation-within-an-observable-chain

Created: 2016-04-05 05:57:56
Updated: 2016-04-05 05:57:56

spring-boot のメトリクスデータを graphite に送る

graphite とはまあザックリ言うと growthforecast みたいなものだが、python で書かれていて、ダッシュボード的な機能が充実してるって感じのものです。

画面の様子は以下の様な感じ。

Graph Tree:

Dashboard:

以下の様な感じで、nc で直接書き込める。http based な gf とはちょっと違う。

PORT=2003
SERVER=graphite.your.org
echo "local.random.diceroll 4 `date +%s`" | nc -q0 ${SERVER} ${PORT}

で、こういうやつにメトリクスデータを送るにはどうしたらいいか。spring-boot 自体はメトリクスの出力先として statsd, redis, opentsdb しかサポートしていない。 statsd から graphite に書き込ませることもできるのだが、今回は直接 graphite に書かせてみた。statsd 経由でやる場合は、各ホストに statsd を動かすってことになるのかなあ。 あんまり statsd 使ってる話を聞かないのでよくわからない。そして、graphite に送るだけなら、直接 graphite に送ればいい気もする。


さて、spring-boot で graphite を送るには、例によって dropwizard-metrics を利用すれば良い。

依存に以下を足す。

compile('io.dropwizard.metrics:metrics-core:3.1.0')
compile('io.dropwizard.metrics:metrics-graphite:3.1.2')

で、実際に動くコードは以下。依存にいれておけば、org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration が動いて、 com.codahale.metrics.MetricRegistry を Autowired できるようになるんで、これを利用すれば良い。

package com.example.config;

import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteReporter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

@Configuration
@Slf4j
public class GraphiteConfig {
    /**
     * {@link MetricsDropwizardAutoConfiguration} configures Dropwizard's MetricRegistry
     * if it's available in classpath.
     */
    @Autowired
    private MetricRegistry registry;

    @PostConstruct
    public void initialize() {
        final Graphite graphite = new Graphite(new InetSocketAddress("localhost", 2003));
        final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry)
                .prefixedWith("web1.example.com")
                .convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS)
                .filter(MetricFilter.ALL)
                .build(graphite);
        reporter.start(1, TimeUnit.MINUTES);

    }
}

簡単ですね。 full sample code is here: https://github.com/tokuhirom/java-samples/tree/master/spring-boot-graphite-demo

Created: 2016-03-29 08:56:30
Updated: 2016-03-29 08:56:30

aspectj の post compile weaving を gradle で行う方法

spring boot の起動が極めて遅くて辛いなと感じていたところ、どうやら spring aop が極めて多くの時間を浪費しているということが判明した。 spring aop をオフにすると起動時間が16秒なのに対し、オンにすると 26 秒に増える。しかも、利用しているエンドポイントの数に比例しているようで、これは今後さらに遅くなりそうだ。。

調べてみると spring aop は load time weaving を採用していることがわかった。 ref. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html

AOP には3種類のやり方がある。

  • Compile-time weaving
    • ajc という eclipse java compiler を modified した版で .java を .class にコンパイルする際に AOP コードをねじ込む
    • lombok は ajc をサポートしていない。
  • Post compile weaving
    • ajc を利用するが、.class から .class への変換を行う。
  • load time weaving
    • 実行時に DI コンテナからの bean 取得時などに、proxy object を挟むことによって、AOP を実現する
    • JDK dynamic proxies を使う方法と CGLib 等によるバイトコード生成を利用する方法がある
    • JDK dynamic proxy は interface には適用可能だが class に対しては適用できない。
    • 実行時にオーバーヘッドがかかる。特に起動時にダミークラス挟まなきゃいけないし、ビーンのロード時にいちいちチェックしないといけない。

ref. https://eclipse.org/aspectj/doc/next/devguide/ltw.html

このうち compile-time weaving は、lombok を利用できないために却下。

そういうわけで、Post compile weaving を試してみた。 しかし、gradle で post compile weaving を利用する方法は、なかなか見つからない。 見つからないのでゴリゴリと書いた。

gradle では、プロジェクト内にプラグインを置くことができるので、aspectj プラグインを書く。 ディレクトリ構成は以下のようにする。

buildSrc
├── build.gradle
└──c
    └── main
        ├── groovy
        │   └── aspectj
        │       └── AspectJPlugin.groovy
        └── resour
            └── METAF
                └── gradle-plugins
                    └── aspectj.properties

resources/META-INF/gradle-plugins/aspectj.properties に、implementation-class=aspectj.AspectJPlugin と書くことで、aspectj plugin の実装がどこにあるのかを指定する。

src/main/groovy/aspetctj/AspectJPlugin.groovy は、class AspectJPlugin implements org.gradle.api.Plugin<org.gradle.api.Project> { } のように、プラグインクラスを継承した、 クラスを配置する。このPluginクラスの void apply(Project project) メソッドが、apply plugin: 'aspectj' された時に呼ばれるので、ここにコードを記載していく。

spring security に入っていた aspectj plugin をベースに書いたが、もはや原型はとどめていないコードがこちらになります。 ``` package aspectj

import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.TaskAction

import java.nio.file.Path

// taken from spring security. // https://github.com/spring-projects/spring-security/blob/master/buildSrc/src/main/groovy/aspectj/AspectJPlugin.groovy /** * * @author Luke Taylor */ class AspectJPlugin implements Plugin {

void apply(Project project) {
    project.plugins.apply(JavaPlugin)

    if (!project.hasProperty('aspectjVersion')) {
        throw new GradleException("You must set the property 'aspectjVersion' before applying the aspectj plugin")
    }

    if (project.configurations.findByName('ajtools') == null) {
        project.configurations.create('ajtools')
        project.dependencies {
            ajtools "org.aspectj:aspectjtools:${project.aspectjVersion}"
            compile "org.aspectj:aspectjrt:${project.aspectjVersion}"
        }
    }

    project.afterEvaluate {

        project.tasks.create(name: 'compileAspect', overwrite: true, description: 'Compiles AspectJ Source', type: Ajc) {
            dependsOn project.processResources, project.compileJava

            tmpDir = "${project.buildDir}/aspect/"
            args = [
                    "-inpath", project.sourceSets.main.output.classesDir.toPath(),
                    "-showWeaveInfo",
                    "-1.8",
                    "-d", tmpDir,
                    "-classpath", project.sourceSets.main.compileClasspath.asPath,
            ];
            dstDir = project.sourceSets.main.output.classesDir.toPath()
        }
        project.tasks.classes.dependsOn project.tasks.compileAspect

        project.tasks.create(name: 'compileTestAspect', overwrite: true, description: 'Compiles AspectJ Test Source', type: Ajc) {
            dependsOn project.processTestResources, project.compileTestJava

            tmpDir = "${project.buildDir}/test-aspect/"
            def classpath = project.sourceSets.test.compileClasspath.files.grep({ it.exists() }).join(":")
            args = [
                    "-inpath", project.sourceSets.test.output.classesDir.toPath(),
                    "-aspectpath", project.sourceSets.main.output.classesDir.toPath(),
                    "-aspectpath", project.sourceSets.test.output.classesDir.toPath(),
                    "-showWeaveInfo",
                    "-1.8",
                    "-d", tmpDir,
                    "-classpath", classpath
            ];
            dstDir = project.sourceSets.test.output.classesDir.toPath()
        }
        project.tasks.testClasses.dependsOn project.tasks.compileTestAspect
    }
}

}

class Ajc extends DefaultTask { String[] args String tmpDir Path dstDir

Ajc() {
    logging.captureStandardOutput(LogLevel.INFO)
}

//http://www.eclipse.org/aspectj/doc/released/devguide/ajc-ref.html

// http://stackoverflow.com/questions/3660547/apt-and-aop-in-the-same-project-using-maven
// https://github.com/uPhyca/gradle-android-aspectj-plugin/blob/8d580d8117932a23209421193da77f175d19d416/plugin/src/main/groovy/com/uphyca/gradle/android/AspectjCompile.groovy
@TaskAction
def compile() {
    logger.info("Running ajc ...")

    MessageHandler handler = new MessageHandler(false);
    logger.info("args: $args")
    new Main().run(args as String[], handler);
    for (IMessage message : handler.getMessages(null, true)) {
        switch (message.getKind()) {
            case IMessage.ABORT:
            case IMessage.ERROR:
            case IMessage.FAIL:
                logger.error message.message, message.thrown
                break;
            case IMessage.WARNING:
                logger.warn message.message, message.thrown
                break;
            case IMessage.INFO:
                logger.info message.message, message.thrown
                break;
            case IMessage.DEBUG:
                logger.debug message.message, message.thrown
                break;
        }
    }

    ant.move(file: tmpDir, tofile: dstDir)
}

} ```

ネットで情報を gradle+aspectj の例を探していると、aspectj の ant task を利用する方法がいくつか出てくるが、aspectj の ant plugin の記述方法を元に gradle で ant task を呼ぶのは、 デバッグしにくいので難しい。aspectj の Main を呼ぶ方法が便利だと思う。 (もちろん、Main を呼ぶのはdocumentedなインターフェースではないので、壊れる可能性があるが、壊れたら別の Main を呼べばいいだけ)

この Main を直接叩く方法は、gradle の android で aspectj するプラグインを参考にした。

まあそういう感じでできるようになったので良かったね、と。

で、load time weaving についても、こんなに遅いわけもないと思うから、もうちょい頑張れば速くなるのかもしれないけど知見がまったくないです。

Created: 2016-03-28 09:23:52
Updated: 2016-03-28 09:23:52

dropwizard metrics servlet で spring-boot app のメトリクスをいい感じに取得する

spring-boot の MetricsFilter はしょぼい。ステータスコードごとのリクエスト数のカウンタと、最後にアクセスされたリクエストの処理時間ぐらいしか取得できない。

これは不便。Dropwizard ならデフォルトで以下の項目を取得可能。

  • status コードごとのrequest の件数のカウンタ
    • 件数/平均リクエスト数/過去1,5,15分の移動平均
    • *.responseCodes.*
  • 現在 web server が処理中の件数
    • "instrumented.activeRequests" : 1,
  • リクエストの平均処理速度など。
    • "instrumented.requests.count" : 2003,
    • "instrumented.requests.fifteenMinuteRate" : 2.2068657173426,
    • "instrumented.requests.fiveMinuteRate" : 6.51051261011083,
    • "instrumented.requests.meanRate" : 37.8061917507824,
    • "instrumented.requests.oneMinuteRate" : 29.5187887307098,
    • "instrumented.requests.snapshot.75thPercentile" : 4,
    • "instrumented.requests.snapshot.95thPercentile" : 18,
    • "instrumented.requests.snapshot.98thPercentile" : 40,
    • "instrumented.requests.snapshot.999thPercentile" : 135,
    • "instrumented.requests.snapshot.99thPercentile" : 72,
    • "instrumented.requests.snapshot.max" : 155,
    • "instrumented.requests.snapshot.mean" : 5,
    • "instrumented.requests.snapshot.median" : 2,
    • "instrumented.requests.snapshot.min" : 0,
    • "instrumented.requests.snapshot.stdDev" : 12,

こういった値を簡単に dropwizard-metrics-servlet を利用して取得するための設定方法を紹介する。

設定方法

依存に以下を入れる。

  compile 'io.dropwizard.metrics:metrics-core:3.1.2'
  compile 'io.dropwizard.metrics:metrics-servlet:3.1.2'

以下のように、Dropwizard Metrics Servlet に入っているフィルタを利用する。これだけ。

package com.example.config;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlet.AbstractInstrumentedFilter;
import com.codahale.metrics.servlet.InstrumentedFilter;
import com.codahale.metrics.servlet.InstrumentedFilterContextListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class InstrumentedFilterConfig {
    @Bean
    public FilterRegistrationBean instrumentedFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MyInstrumentedFilter());
        registration.addInitParameter("name-prefix", "instrumented");
        return registration;
    }

    public static class MyInstrumentedFilter extends AbstractInstrumentedFilter {
        private static final String NAME_PREFIX = "responseCodes.";
        private static final int OK = 200;
        private static final int CREATED = 201;
        private static final int NO_CONTENT = 204;
        private static final int BAD_REQUEST = 400;
        private static final int NOT_FOUND = 404;
        private static final int SERVER_ERROR = 500;

        /**
         * Creates a new instance of the filter.
         */
        public MyInstrumentedFilter() {
            super(InstrumentedFilter.REGISTRY_ATTRIBUTE,
                    createMeterNamesByStatusCode(),
                    NAME_PREFIX + "other");
        }

        private static Map<Integer, String> createMeterNamesByStatusCode() {
            final Map<Integer, String> meterNamesByStatusCode = new HashMap<Integer, String>(6);
            meterNamesByStatusCode.put(OK, NAME_PREFIX + "ok");
            meterNamesByStatusCode.put(CREATED, NAME_PREFIX + "created");
            meterNamesByStatusCode.put(NO_CONTENT, NAME_PREFIX + "noContent");
            meterNamesByStatusCode.put(BAD_REQUEST, NAME_PREFIX + "badRequest");
            meterNamesByStatusCode.put(NOT_FOUND, NAME_PREFIX + "notFound");
            meterNamesByStatusCode.put(SERVER_ERROR, NAME_PREFIX + "serverError");
            return meterNamesByStatusCode;
        }
    }

    // Pass MetricRegistry configured by Spring boot to InstrumentedFilter.
    @Bean
    public MyListener myListener() {
        return new MyListener();
    }

    public static class MyListener extends InstrumentedFilterContextListener {
        @Autowired
        private MetricRegistry metricRegistry;

        @Override
        protected MetricRegistry getMetricRegistry() {
            return metricRegistry;
        }
    }
}

これだけ設定すると、Actuator の /metrics に、以下の様な出力が出るようになる。


{
   "classes" : 5922,
   "classes.loaded" : 5922,
   "classes.unloaded" : 0,
   "counter.status.200.hello.id" : 1000,
   "counter.status.200.metrics" : 3,
   "counter.status.404.star-star" : 1000,
   "gauge.response.hello.id" : 3,
   "gauge.response.metrics" : 24,
   "gauge.response.star-star" : 105,
   "gc.ps_marksweep.count" : 1,
   "gc.ps_marksweep.time" : 85,
   "gc.ps_scavenge.count" : 9,
   "gc.ps_scavenge.time" : 151,
   "heap" : 1864192,
   "heap.committed" : 274944,
   "heap.init" : 131072,
   "heap.used" : 205912,
   "httpsessions.active" : 0,
   "httpsessions.max" : -1,
   "instance.uptime" : 52108,
   "instrumented.activeRequests" : 1,
   "instrumented.requests.count" : 2003,
   "instrumented.requests.fifteenMinuteRate" : 2.2068657173426,
   "instrumented.requests.fiveMinuteRate" : 6.51051261011083,
   "instrumented.requests.meanRate" : 37.8061917507824,
   "instrumented.requests.oneMinuteRate" : 29.5187887307098,
   "instrumented.requests.snapshot.75thPercentile" : 4,
   "instrumented.requests.snapshot.95thPercentile" : 18,
   "instrumented.requests.snapshot.98thPercentile" : 40,
   "instrumented.requests.snapshot.999thPercentile" : 135,
   "instrumented.requests.snapshot.99thPercentile" : 72,
   "instrumented.requests.snapshot.max" : 155,
   "instrumented.requests.snapshot.mean" : 5,
   "instrumented.requests.snapshot.median" : 2,
   "instrumented.requests.snapshot.min" : 0,
   "instrumented.requests.snapshot.stdDev" : 12,
   "instrumented.responseCodes.badRequest.count" : 0,
   "instrumented.responseCodes.badRequest.fifteenMinuteRate" : 0,
   "instrumented.responseCodes.badRequest.fiveMinuteRate" : 0,
   "instrumented.responseCodes.badRequest.meanRate" : 0,
   "instrumented.responseCodes.badRequest.oneMinuteRate" : 0,
   "instrumented.responseCodes.created.count" : 0,
   "instrumented.responseCodes.created.fifteenMinuteRate" : 0,
   "instrumented.responseCodes.created.fiveMinuteRate" : 0,
   "instrumented.responseCodes.created.meanRate" : 0,
   "instrumented.responseCodes.created.oneMinuteRate" : 0,
   "instrumented.responseCodes.noContent.count" : 0,
   "instrumented.responseCodes.noContent.fifteenMinuteRate" : 0,
   "instrumented.responseCodes.noContent.fiveMinuteRate" : 0,
   "instrumented.responseCodes.noContent.meanRate" : 0,
   "instrumented.responseCodes.noContent.oneMinuteRate" : 0,
   "instrumented.responseCodes.notFound.count" : 1000,
   "instrumented.responseCodes.notFound.fifteenMinuteRate" : 1.1078032687181,
   "instrumented.responseCodes.notFound.fiveMinuteRate" : 3.3036876086696,
   "instrumented.responseCodes.notFound.meanRate" : 18.8739613014361,
   "instrumented.responseCodes.notFound.oneMinuteRate" : 15.9438096464588,
   "instrumented.responseCodes.ok.count" : 1003,
   "instrumented.responseCodes.ok.fifteenMinuteRate" : 1.09906858728133,
   "instrumented.responseCodes.ok.fiveMinuteRate" : 3.20687964000899,
   "instrumented.responseCodes.ok.meanRate" : 18.929889559489,
   "instrumented.responseCodes.ok.oneMinuteRate" : 13.5762576633774,
   "instrumented.responseCodes.other.count" : 0,
   "instrumented.responseCodes.other.fifteenMinuteRate" : 0,
   "instrumented.responseCodes.other.fiveMinuteRate" : 0,
   "instrumented.responseCodes.other.meanRate" : 0,
   "instrumented.responseCodes.other.oneMinuteRate" : 0,
   "instrumented.responseCodes.serverError.count" : 0,
   "instrumented.responseCodes.serverError.fifteenMinuteRate" : 0,
   "instrumented.responseCodes.serverError.fiveMinuteRate" : 0,
   "instrumented.responseCodes.serverError.meanRate" : 0,
   "instrumented.responseCodes.serverError.oneMinuteRate" : 0,
   "mem" : 325332,
   "mem.free" : 69031,
   "nonheap" : 0,
   "nonheap.committed" : 51392,
   "nonheap.init" : 2496,
   "nonheap.used" : 50388,
   "processors" : 4,
   "systemload.average" : 3.869140625,
   "threads" : 24,
   "threads.daemon" : 22,
   "threads.peak" : 24,
   "threads.totalStarted" : 28,
   "uptime" : 60801
}

パフォーマンス

実際のところ、こんなに細かく数字とったらパフォーマンス劣化しないの?と気にする貧乏性の人もいると思う。 しかし実際、これは以下の程度のパフォーマンスがでるので、十分速いので気にしなくてよい。

Score:

benchHistogram:  1 wallclock secs ( 1.12 usr +  0.08 sys =  1.20 CPU) @ 3801478.48/s (n=4573152)
benchTimer:  1 wallclock secs ( 1.02 usr +  0.01 sys =  1.03 CPU) @ 3164606.01/s (n=3264693)
benchMetrics:  1 wallclock secs ( 1.15 usr +  0.04 sys =  1.19 CPU) @ 25211790.53/s (n=30020990)

Comparison chart:

                        Rate  benchHistogram  benchTimer  benchMetrics
  benchHistogram   3801478/s              --         20%          -85%
      benchTimer   3164606/s            -17%          --          -87%
    benchMetrics  25211791/s            563%        697%            --

まとめ

Dropwizard metrics を利用することで spring boot application のメトリクスを詳細に取得する方法について紹介した。 実際に動作するサンプルを以下のレポジトリに配置したのでご利用ください。

https://github.com/tokuhirom/spring-boot-dropwizard-metrics-filter-sample

SEE ALSO

参考: http://blog.64p.org/entry/2016/03/21/205935

Created: 2016-03-24 23:05:40
Updated: 2016-03-24 23:05:40

How do I set TemplateExceptionHandler with spring-boot+freemarker?

You should use one of following properties.

-Dspring.freemarker.template_exception_handler=rethrow
-Dspring.freemarker.template_exception_handler=debug
-Dspring.freemarker.template_exception_handler=html_debug

Thanks,

Created: 2016-03-24 18:00:35
Updated: 2016-03-24 18:00:35

jspm で angular2 を使ってみる。

jspm+systemjs を angular2 では推してるっぽいし使ってみるかとおもって使ってみるとなかなか一筋縄ではいかない。 ベータ! という感じがしてよい。

そういうわけで、jspm+systemjs でやってみた記録です。

node はすでにセットアップ済みという前提です。

npm install -g typescript jspm

として、必要なツールをインストールする。

次に jspm の初期設定をする。

$ jspm init
Package.json file does not exist, create it? [yes]:
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:
Enter server baseURL (public folder path) [./]:
Enter jspm packages folder [./jspm_packages]:
Enter config file path [./config.js]:
Configuration file config.js doesn't exist, create it? [yes]:
Enter client baseURL (public folder URL) [/]:
Do you wish to use a transpiler? [yes]:
Which ES6 transpiler would you like to use, Babel, TypeScript or Traceur? [babel]:typescript
(以下略)

なんかいろいろ聞かれてセットアップが完了する。

次に依存モジュールをインストールする。

jspm install angular2 reflect-metadata zone.js es6-shim typescript

config.js の中身を以下の様な感じにいじる。そんなに意味不明な設定はない。

System.config({
    baseURL: "/",
    defaultJSExtensions: true,
    transpiler: "typescript",
    typescriptOptions: {
        "module": "commonjs",
        "emitDecoratorMetadata": true
    },
    paths: {
        "github:*": "jspm_packages/github/*",
        "npm:*": "jspm_packages/npm/*"
    },

    packages: {
        "app": {
            "main": "main",
            "defaultExtension": "ts"
        }
    },

    map: {
        /* ここは自動生成される */
    }
});

で、app/main.ts に以下のように書く。基本的には、通常の angular2 アプリと一緒なのだが、依存を明示しないといけない点があってダサい。 この依存は config.js の中に 'meta' 項目として記載することも可能なのだが、まあ import を明示的にしておいたほうがわかりやすいだろうということで import した。

//import deps
import 'zone.js';
import 'zone.js/dist/long-stack-trace-zone';

import 'reflect-metadata';
import 'es6-shim';

// app code
import {Component} from 'angular2/core';
import {bootstrap} from "angular2/platform/browser";

@Component({
    selector: 'test-app',
    template: '<h4>Hello {{name}}</h4>'
})
class TestApp {
    name: string;

    constructor(){
        this.name = 'Angular2';
        setTimeout(() => {
        this.name = 'Angular2!!!'
        },1500);
    }
}

//start our app
bootstrap(TestApp);

最後に index.html に以下のように書く。

<html>
<head>
    <title>Demo App</title>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
</head>
<body>
    <test-app>
        Loading...
    </test-app>

    <script>
        System.import('app');
    </script>
</body>
</html>

bundle

jspm bundle-sfx --minify app

とするだけで bundle 版が作成可能。bundle-sfx で作成した場合、system.js も同梱されてるので、取り扱いが楽。

で、production 環境では以下のように、build.js だけを読めばOK.

<html>
<head>
    <title>Demo App</title>
</head>
<body>
    <test-app>
        Loading...
    </test-app>
    <script src="build.js"></script>
</body>
</html>

今後の展望

java app での良い感じの取り回しについて考えていきたい。

たとえば以下のような構成にするのが良いのではないかと考える。

src/main/java
src/main/resources/static/index.html ← build.js を読む
front/app/main.ts
front/index.html                     ← config.js 読んで、動的に処理

で、web server は file:front/,classpath:/static/ を static file として扱うようにして、gradle で jspm をキックして、build/resources/static/build.js を生成すればよい。

というところまで考えたのでよろしくお願いします。

まとめ

JS 系のツールは、なんかいろいろと設定が膨らみがちだが、jspm は最小限の設定ですむのでいい感じっぽい。 (最初 webpack でやろうと思っていたが、jspm の方が楽だった)

Java アプリとの連携については、ある程度、こんな感じでやればできるなあという感触をつかめた。

どうしてもブラウザでの確認がめんどくさいので、Ajax 通信部分のモッキング含めて、いい感じにテストできるようにしたい。そのへんもちゃんと読まないとな。 https://angular.io/docs/ts/latest/guide/testing.html

あと、本稿の内容は以下の gist の内容を参考に最新版でも動くようにしたものです。 https://gist.github.com/robwormald/429e01c6d802767441ec

Created: 2016-03-22 08:51:32
Updated: 2016-03-22 08:51:32

spring boot アプリケーションのMetrics値取得について

Spring boot の metrics 機能について

spring boot には metrics 機能が標準でついている。metrics では Counter と Gauge の2種類のメトリクスを食わせることが可能である。

Counter は、単純に増加/現象する値を表すのに使うメトリクス値である。 たとえば、アクセスの件数、エラーの件数、現在利用中の値を取得したりするのに使う。

サンプルコードは以下の通り。

@Service
public class FooService {
    @AutoWired
    private CounterService counterService;
    @Autowired
    private FooRepository fooRepository;

    public Foo get(String key) {
        counterService.increment("foo");
        return fooRepository.get(key);
    }
}

Gauge は、単純に数値を渡すのに使う。普通に使うと、直近の一個の値がそのまま metrics 数字を取得できるだけなので、利用方法がちょっと思いつかない。 サンプルコードとしては以下の様な感じで利用される。

gaugeService.set("foo", 3.14);

Metrics 値の利用

Actuator での閲覧

特に設定せずに spring boot アプリケーションを起動すると、http://localhost:8080/metrics からアプリケーションのメトリクス値が取得可能になっている。

ここを閲覧して、変な値が出ていないか確認することができる。 ここを cron で取得して、alert を上げるなどの利用法が行われている。と思う。

外部レポジトリへの送信

spring-boot の metrics では、データを外部のミドルウェアに送信することができる。以下のミドルウェアに送信するための exporter が標準添付されている。

  • graphite
  • open tsdb
  • redis

gauge の値は actuator で確認するぶんには、最後の一個が見れるだけなので、使いにくいが、各値を外部 middleware に保存することで可能性が広がる。

Dropwizard Metrics との統合

dropwizard という web application framework には metrics という似たようなしくみがあるのだが、dropwizard の metrics の方がよく作りこまれている。。んですよ。 で、spring-boot の metrics は、classpath に dropwizard metrics があれば、そっちを使うように実装が差し替わります。

Dropwizard Metrics を利用するようにすると、gauge を取得するときに、prefix に 'histogram.' あるいは 'timer.' をつけると、生の値ではなく、min/max/stddev/percentile などの値が取得できるようになります。

Dropwizard metrics では、高速にメトリクス集計することが可能。Reservoir というコンポーネントで、集計を行っている。 Reservoir は性質のことなるによって幾つかの実装が存在している。

Uniform Reservoirs

Uniform Reservoirs は、Vitter's Algorithm R ってやつでランダムサンプリングして集計している。 長期間の間のメトリクスを取るのに向いてる。データの傾向が変わったことを検出するのとかには向いてない。

Exponentially Decaying Reservoirs

http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf 詳しくはこのへん。

過去、約5分間の傾向をしる事ができる。メトリクス傾向の変化を即座に検知したいなんてときにはこれ。 spring-boot-actuator の histogram 実装はこれをデフォルトとして選択している。 とりあえずこれをそのまま使っておけばいい。と思う。

Sliding Window Reservoirs

過去N個のデータを利用して値を出す

Sliding Time Window Reservoirs

過去N秒のデータを利用して結果を出す。データの流量が多いとメモリ量めっちゃ食って死ぬ。

Created: 2016-03-21 21:13:02
Updated: 2016-03-21 21:13:02

oh-my-zsh やめて zplug にしようかと思ったけどやっぱり zgen にした

mput さんの記事→ https://moneyforward.com/engineers_blog/2016/03/15/dotfiles/ を見て、zplug というものの存在を知った。

zplug を設定しようと思ったが、そもそも zsh の設定をこまごまとやるのがあんま好きじゃないし、コマンドのインストールとかまで出来るのが zplug だということで、ややオーバースペックに感じた。 いろいろ見ていると zgen というのが好みにあってる感じっぽかったので zgen にした。

source "${HOME}/.zgen/zgen.zsh"

if ! zgen saved; then
    zgen oh-my-zsh

    zgen oh-my-zsh plugins/git
    zgen oh-my-zsh plugins/ssh-agent

    zgen load zsh-users/zsh-syntax-highlighting
    zgen load "zsh-users/zsh-completions"

    if [[ $HOST = 'www3301gi.sakura.ne.jp' ]]; then
        zgen oh-my-zsh themes/clean
    else
        zgen oh-my-zsh themes/sonicradish
    fi

    zgen save
fi

oh-my-zsh の機能もそんなに使ってなかったし、まあこれでいいかという感じ。

mput さんがおすすめしていた predict on も試してみたが、git stash ってウトウトしたらうっかり git stash pop 実行してしまったりして、イライラするばかりだったのでやめました。

Created: 2016-03-20 13:19:50
Updated: 2016-03-20 13:19:50
Next page