Posted at

Zipkin導入最小ステップ

More than 1 year has passed since last update.


まえがき

既存のSpring BootプロジェクトへのZipkin導入をできるだけ少ないステップでまとめてみました。

サービス間のやり取りの結果についても書きたかったですが、別に何も意識せずとも勝手にやってくれるので、今回の記事では言及しません。

HTTP周りのやり取りはspring-mvc, RestTemplateを使っていれば勝手にやってくれます。


Step1 依存の追加


Maven


Spring IO Platform を使っている場合


pom.xml

<properties>

<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!-- Kafka経由で送る場合はこれも必要 -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
</dependencies>



Spring Boot のみの場合

公式リファレンスを参照してください。


Gradle

公式リファレンスを参照してください。


Step2 設定値の追加

Localマシンに全てデフォルトポートで構築済みであれば不要です。


HTTP送信


application.yaml

spring:

zipkin:
base-url: http://localhost:9411/ # default


Kafka送信


application.yaml

spring:

kafka:
bootstrap-servers: localhost:9092 # default


Step3 動作確認環境構築

Mac前提です。


Kafka & Zookeepr

$ brew install kafka

$ brew services start zookeeper
$ brew services start kafka


Zipkin

$ wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'


HTTP経由のみ

$ java -jar zipkin.jar


Kafka対応

$ KAFKA_ZOOKEEPER=localhost:2181 java -jar zipkin.jar


Step4 動作確認

@RequestMapping("/hello")

String hello() {
return "hello world"
}

同作確認なので全てのリクエストをZipkinに送ります。


application.yaml

spring:

sleuth:
sampler:
percentage: 1.0 # 昔の名残でプロパティ名がパーセンテージになっていますが、割合指定らしいです。

スクリーンショット 2018-02-20 13.19.55.png

http://localhost:9411/にアクセスする。

スクリーンショット 2018-02-20 13.21.58.png

スクリーンショット 2018-02-20 13.22.05.png

スクリーンショット 2018-02-20 13.23.30.png

ここまでの手順は依存ライブラリを追加しただけです。

送信先がデフォルトでない場合は、送信先のを指定をします。


Step5 Spanの追加

これだけではリクエスト受信からレスポンス返却までの時間しかわかりません。

もう少しSpanを増やしてみます。

@Autowired

DemoService demoService;

@RequestMapping("/hello")
public String helloWorld() throws Exception {
Thread.sleep(300);
return demoService.greeting("world");
}

@Service
static class DemoService {

@NewSpan("greeting")
String greeting(String target) throws Exception {
Thread.sleep(200);
return "Hello " + target;
}
}

スクリーンショット 2018-02-20 13.45.47.png


タグの追加

@Service

static class DemoService {

@NewSpan("greeting")
String greeting(@SpanTag("target") String target) throws Exception {
Thread.sleep(200);
return "Hello " + target;
}
}

@SpanTag("target") をパラメータに追加しただけです。

スクリーンショット 2018-02-20 13.50.06.png


色々な型のタグ

Zipkinはタグの値を文字列でしか受け取れないので、JSONにできるならそうして送ると良いです。

@NewSpan("greeting-name")

public String greeting(@SpanTag(value = "name", resolver = AsJson.class) Name name) throws Exception {
Thread.sleep(200);
return "Hello " + name + ".";
}


AsJson.java

@Component

public class AsJson implements TagValueResolver {

ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.indentOutput(true)
.build();

@Override
public String resolve(Object parameter) {
if (parameter == null) {
return null;
}
if (String.class.isAssignableFrom(parameter.getClass())) {
return (String) parameter;
}

try {
return objectMapper.writeValueAsString(parameter);
} catch (JsonProcessingException e) {
return null;
}
}
}


@SpanTag(key = "name") だと手元の環境だと認識されなかったので、value =で送りました。

@Aliasの処理ができていないのか、後でコードを追ってみます。

JSONタグを確認してみます。

スクリーンショット 2018-02-20 14.02.19.png

大丈夫そうです。


トラブルシューティング


ZipkinサーバーがV1でJSONフォーマットが違うんだけど。

JSONフォーマットはバージョン指定ができます。


application.yaml

spring:

zipkin:
encoder: json_v1


Zipkinサーバーでサービス名のラベルが空文字列で表示される

V1のZipkinはラベル表示に使用するサービス名を規定のタグでしか見ないようです。

カスタムタグのみのSpanではサービス名を見てくれないらしいです。

https://github.com/openzipkin/zipkin/issues/962

そこで適当にコンポーネント名を設定しておきます。

@Bean

@ConditionalOnProperty(name = "spring.zipkin.encoder", havingValue = "json_v1")
SpanAdjuster spanAdjuster() {
return span -> {
span.tag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, "my-component");
return span;
};
}

これで大丈夫です。


非同期処理で途切れる

公式リファレンスを参照してください。

@Asyncではデフォルトで途切れない設定になっていると思います。

ExecutorServiceを使っている場合は、

ExecutorService traceableExecutorService = new TraceableExecutorService(beanFactory, executorService);

として、使えば大丈夫です。


処理の途中でタグを追加したい

アノテーションで指定できないタイミングでタグを追加したい時があります。

@Autowired

Tracer tracer

public void something() {
tracer.addTag("$name", "$value");
}

これでOKです。現在のSpanにタグ付けされます。