LoginSignup
0
1

More than 3 years have passed since last update.

Spring Cloudでマイクロサービスを構成する(5):トレーシング編

Last updated at Posted at 2021-02-05

Spring Cloudシリーズの第5回です。

概要

今回は「OpenTracing API」と「Jaeger(イェーガー)」を使ってリクエストをトレーシング(追跡)してみましょう。

services.png

前回までで、上記の構成を作りましたが、コンポーネントやサービスが多くなったり、冗長化して負荷分散したりすると、1つのリクエスト(トランザクション)がどこのインスタンスを通り、それぞれどれだけの処理時間を要しているのかが、わかりにくくなります。
トレーシング技術を使うことによって、それらを可視化することができます。

環境

以下が動く環境を前提とします。

  • docker & docker-compose
  • JDK11

前回までの投稿の続きですので、前回のゴール地点のソースを手元に用意し、それをベースとします。

作業ステップ

本記事では以下のステップで進めていきます。

  1. Jaegarサービスの追加
  2. トレーシングの設定追加(Dependencyの追加と設定ファイルの記述)
  3. R2DBCのトレーシングの追加

ステップ1:Jaegarサービスの追加

Jaegerの全部入りdockerイメージが用意されていますので、docker-compose.ymlに追加するだけです。実にお手軽に開発環境を構築できますね。

docker-compose.yml
volumes:
  pgdata:
networks:
  default:
services:
  db:
    ・・・
  adminer:
    ・・・
  gw:
    ・・・
   ・・・
  jaeger:
    image: jaegertracing/all-in-one
    container_name: jaegar
    environment:
      - COLLECTOR_ZIPKIN_HTTP_PORT=9411
    ports:
      - 5775:5775/udp
      - 6831:6831/udp
      - 6832:6832/udp
      - 5778:5778
      - 16686:16686
      - 14268:14268
      - 9411:9411
    networks:
      - default

以下で起動・停止ができます。

(起動)
$ docker-compose up -d jaeger
(停止)
$ docker-compose stop jaeger

ステップ2:トレーシングの設定追加(Dependencyの追加と設定ファイルの記述)

今回もあまりやることがありません。2つだけ。

まず1つ目。pom.xmlにdependencyを追加します。

Spring Cloud Gateway には opentracing-spring-jaeger-cloud-starter を追加します。

gateway/pom.xml
    <dependencies>
                ・・・
        <dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
                ・・・
    </dependencies>

Account には opentracing-spring-jaeger-web-starter を追加します。

account/pom.xml
    <dependencies>
                ・・・
        <dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-jaeger-web-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
                ・・・
    </dependencies>

やること2つ目。
設定ファイル「application.yml」にJaegerへの接続設定を追加します。

ここではHTTPでJaegerにトレーシング情報を送信する設定を追加しています。
JaegerへのリクエストURLは環境変数で上書きできるようにしておきましょう。

gateway/src/main/resources/application.yml
server:
  ・・・
spring:
  ・・・
management:
  ・・・
eureka:
  ・・・
opentracing:
  jaeger:
    log-spans: false
    http-sender:
       url: ${JAEGER_HTTP_URL:http://localhost:14268/api/traces}

account/src/main/resources/application.yml にも上記と同様の「opentracing」の定義を追加します。

docker-compose.ymlで環境変数「JAEGER_HTTP_URL」を定義します。
docker-composeではサービス名をホスト名にしておくと、よしなに名前解決してくれるので、「jaeger」をホスト名に指定します。

docker-compose.yml
 ・・・

  gw:
    ・・・
    environment:
      ・・・
      - JAEGER_HTTP_URL=http://jaeger:14268/api/traces

  account:
    ・・・
    environment:
      ・・・
      - JAEGER_HTTP_URL=http://jaeger:14268/api/traces
  account2:
    ・・・
    environment:
      ・・・
      - JAEGER_HTTP_URL=http://jaeger:14268/api/traces

  account3:
    ・・・
    environment:
      ・・・
      - JAEGER_HTTP_URL=http://jaeger:14268/api/traces

ここまでで、とくに追跡コードなしに自動的にgateway-->accountの範囲でトレーシングができるようになります。
親切な世の中です。

$ cd account
$ ./mvnw clean package -DskipTests=true
$ docker-compose up -d --build account

http://localhost:5000/api/account にアクセスした後に、
http://localhost:16686/ にアクセスし、リクエストを選択すると以下のように呼び出し階層が見えると思います。

jaeger2.png

ステップ3:R2DBCのトレーシングの追加

R2DBCを使ったDBアクセスもトレーシングできるようにしてみましょう。
これでDBアクセスがどのタイミングでどのくらい処理時間がかかっているかが把握できるようになります。
R2DBC+Springbootで自動的にトレーシング情報を送信するコンポーネントはまだ存在しないようなので、現在公開されている情報を頼りに少し手作りします。

以下を行います。

spring-msa
├── account
│   └── src/main/java/org/example/account
│       ├── AccountApplication.java
│       ├── AccountConfiguration.java  <-- ★新規追加【2】
│       ├── controller
│       ├── entity
│       ├── repository
│       └── TracingExecutionListener.java  <-- ★新規追加【1】
├── db
├── docker-compose.yml
├── gateyway
└── sd 

【1】TracingExecutionListener

R2DBCの公式サイトにあるExample をベースにし、OpenTracing APIを使うように加工する。

ソースはこちら
account/src/main/java/com/example/account/TracingExecutionListener.java

【2】AccountConfiguration

account/src/main/java/com/example/account/AccountConfiguration.java
package com.example.account;

import io.r2dbc.pool.ConnectionPool;
import io.r2dbc.pool.ConnectionPoolConfiguration;
import io.r2dbc.proxy.ProxyConnectionFactory;
import io.r2dbc.proxy.listener.LifeCycleListener;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBuilder;
import org.springframework.boot.autoconfigure.r2dbc.EmbeddedDatabaseConnection;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;

@Configuration(proxyBeanMethods = false)
public class AccountConfiguration {

    @Bean
    public LifeCycleListener tracingExecutionListener() {
        return new TracingExecutionListener();
    }

    // https://github.com/spring-projects/spring-boot/pull/21689
    // 上記のPull Request が取り込まれたら、以下のコードは不要となる
    @Bean
    public ConnectionFactory connectionFactory(R2dbcProperties properties,
                                        ResourceLoader resourceLoader,
                                        LifeCycleListener tracingExecutionListener) {

        ConnectionFactory original = ConnectionFactoryBuilder
                .of(properties, () ->
                        EmbeddedDatabaseConnection.get(resourceLoader.getClassLoader())).build();
        ConnectionFactory proxyConnectionFactory =
                ProxyConnectionFactory.builder(original).listener(tracingExecutionListener).build();

        R2dbcProperties.Pool pool = properties.getPool();
        ConnectionPoolConfiguration.Builder builder
                = ConnectionPoolConfiguration.builder(proxyConnectionFactory)
                .maxSize(pool.getMaxSize()).initialSize(pool.getInitialSize()).maxIdleTime(pool.getMaxIdleTime());
        if (StringUtils.hasText(pool.getValidationQuery())) {
            builder.validationQuery(pool.getValidationQuery());
        }
        return new ConnectionPool(builder.build());
    }
}

さて、これでビルドして動かしてみましょう。

$ cd account
$ ./mvnw clean package -DskipTests=true
$ docker-compose up -d --build account

http://localhost:5000/api/account にアクセスした後に、
http://localhost:16686/ にアクセスし、リクエストを選択すると以下のように呼び出し階層が見えると思います。
R2DBCのクエリ内容も見えてますね。

jaeger3.png

さいごに

これまでと同様に、ここまでのソースを以下に置いています。

さて、ここからはPrometheusを立てて、JVMやコンテナのモニタリングをしてみたいところですが、
この記事シリーズはいったんここまでとさせていただきます。

また別の記事を投稿予定ですので、そちらをお待ちください。
ではでは。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1