11.2.1. AWSにおける分散トレーシング

AWSでは AWS X-Ray(外部サイト) という分散トレーシングを実現するためのサービスが用意されている。 Javaアプリケーションの分散トレーシングは以下2つの方法で実現できる。

  • AWS X-Ray SDK for Java
  • AWS X-Ray Java 用 自動計測エージェント

自動計測エージェント(外部サイト) を使用するとアプリケーションのランタイムにコードを追加することなく計測が可能だが、 Nablarchはフレームワークの構造上、自動計測エージェントを使用することができない。そのため本手順ではAWS X-Ray SDK for Javaをアプリケーションに組み込む方法について説明する。 以下で触れない詳細な設定方法については AWS X-Ray SDK for Java(外部サイト) を参照。

重要

2020年10月に第3の方法として AWS Distro for OpenTelemetry(外部サイト、英語) が発表された。 しかし、2021年3月現在production-readyとなっているが、正式リリースはされていない。 そのため今後 AWS Distro for OpenTelemetry がリリースされ、Nablarchでの動作確認がとれた場合は、本章は AWS Distro for OpenTelemetry を使用した手順に差し替える可能性がある。

以下にコンテナ用アーキタイプを使用した場合の例を示す。 受信HTTPリクエスト の設定だけでサービス間の関連はトレースできる。 送信HTTP呼び出しSQLクエリ はアプリケーションの要件に応じて設定する必要がある。

11.2.1.1. 依存関係の追加

AWS X-Ray SDKのサブモジュールから必要なものを依存関係に追加する。 使用できるサブモジュールは以下参照。

pom.xml に以下を追記する。

<dependencyManagement>
  <dependencies>
    <!--  中略  -->
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-xray-recorder-sdk-bom</artifactId>
      <version>2.4.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <!--  中略  -->
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-xray-recorder-sdk-core</artifactId>
  </dependency>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-xray-recorder-sdk-apache-http</artifactId>
  </dependency>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-xray-recorder-sdk-sql</artifactId>
  </dependency>
</dependencies>

補足

AWS X-Ray SDK for Java(外部サイト) ではSQLクエリのトレースには aws-xray-recorder-sdk-sql-postgres または aws-xray-recorder-sdk-sql-mysql を使用すると記載されている。 本手順では任意のJDBCデータソースをトレース可能な aws-xray-sdk-java(外部サイト、英語) に含まれる aws-xray-recorder-sdk-sql を使用する。

11.2.1.2. 受信HTTPリクエスト

受信HTTPリクエストをトレースするためX-Ray サーブレットフィルタをアプリケーションに追加する。 src/main/webapp/WEB-INF/web.xml に以下を追記。

<filter>
  <filter-name>AWSXRayServletFilter</filter-name>
  <filter-class>com.amazonaws.xray.javax.servlet.AWSXRayServletFilter</filter-class>
  <init-param>
    <param-name>fixedName</param-name>
    <!-- サービスマップでアプリケーションを識別する名前を指定する -->
    <param-value>example-app</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>AWSXRayServletFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- ↑既存のfilter-mappingより上に記載する -->
<filter-mapping>
  <filter-name>entryPoint</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

11.2.1.3. 送信HTTP呼び出し

他のサービスへのHTTPリクエストをトレースするための設定を追加する。

X-Ray SDK for Javaには、送信HTTP呼び出しを計測するためのAPIとして Apache HttpComponents(外部サイト、英語) のインタフェースで使用できるクラスが用意されている。

Apache HttpComponentsを直接使うと処理が煩雑になるため、本手順ではJAX-RSクライアントの実装である Jersey(外部サイト、英語) 経由で利用する。 Jerseyは、デフォルトではHTTP通信に java.net.HttpURLConnection を利用するため、Apache HttpComponentsを利用するためには設定が必要となる。 org.glassfish.jersey.client.spi.ConnectorProvider というインタフェースが用意されているので、 その実装クラスをJerseyクライアントに登録することで java.net.HttpURLConnection 以外の方法でHTTP通信が可能となる。

Apache HttpComponentsを利用するための org.glassfish.jersey.client.spi.ConnectorProvider の実装として org.glassfish.jersey.apache.connector.ApacheConnectorProvider が用意されている。

まず依存にJerseyを加える。

<dependencyManagement>
  <dependencies>
    <!--  中略  -->
    <dependency>
      <groupId>org.glassfish.jersey</groupId>
      <artifactId>jersey-bom</artifactId>
      <version>2.32</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <!--  中略  -->
  <!-- Jerseyクライアント -->
  <dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.connectors</groupId>
    <artifactId>jersey-apache-connector</artifactId>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
  </dependency>
</dependencies>

Jerseyには org.glassfish.jersey.apache.connector.ApacheHttpClientBuilderConfigurator インタフェースが用意されている。 このインタフェースを使用することで、 HttpClientBuilder に追加の設定を行うことや、 HttpClientBuilder そのものを差し替えるといった処理が可能となる。 下記では HttpClientBuilder をAWS SDKの com.amazonaws.xray.proxies.apache.http.HttpClientBuilder に差し替えている。

package com.example;

import com.amazonaws.xray.proxies.apache.http.HttpClientBuilder;
import nablarch.core.repository.di.ComponentFactory;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.apache.connector.ApacheHttpClientBuilderConfigurator;
import org.glassfish.jersey.client.ClientConfig;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Configuration;
import java.util.function.UnaryOperator;

public class JerseyHttpClientWithAWSXRayFactory implements ComponentFactory<Client> {
    @Override
    public Client createObject() {
        ApacheHttpClientBuilderConfigurator clientBuilderConfigurator
                = (httpClientBuilder) -> HttpClientBuilder.create();

        Configuration config = new ClientConfig()
                .register(clientBuilderConfigurator)
                .connectorProvider(new ApacheConnectorProvider());
        return ClientBuilder.newClient(config);
    }
}

ComponentFactorysrc/main/resources/rest-component-configuration.xml に記述し、HTTPクライアントをシステムリポジトリに登録する。

<!-- HTTPクライアントの設定 -->
<component name="httpClient" class="com.example.system.httpclient.JerseyHttpClientWithAWSXRayFactory" />

システムリポジトリに登録したHTTPクライアントを使用するクラスの例を以下に示す。 このクラスは @SystemRepositoryComponent のアノテーションを付与することでDIコンテナの構築対象となり、コンストラクタインジェクションでHTTPクライアントが登録される。

package com.example;

import nablarch.core.repository.di.config.externalize.annotation.ComponentRef;
import nablarch.core.repository.di.config.externalize.annotation.ConfigValue;
import nablarch.core.repository.di.config.externalize.annotation.SystemRepositoryComponent;
import javax.ws.rs.client.Client;

@SystemRepositoryComponent
public class HttpProductRepository {

    private final Client httpClient;
    private final String productAPI;

    public HttpProductRepository(@ComponentRef("httpClient") Client httpClient,
                                @ConfigValue("${api.product.url}") String productAPI) {
        this.httpClient = httpClient;
        this.productAPI = productAPI;
    }

    public ProductList findAll() {
        WebTarget target = httpClient.target(productAPI).path("/products");
        return target.request().get(ProductList.class);
    }

    //以下省略
}

また、システムリポジトリから直接HTTPクライアントを取得して使用することも可能。

Client httpClient = SystemRepository.get("httpclient");
WebTarget target = httpClient.target(productAPI).path("/products");
ProductResponse products = target.request().get(ProductResponse.class);

11.2.1.4. SQLクエリ

SQLクエリを計測対象とするための設定を加える。

以下に記載のように、データソースを com.amazonaws.xray.sql.TracingDataSource でデコレートすることでSQLクエリの計測が可能となる。

デコレートされたデータソースを作成するファクトリクラスを作成する。

package com.example;

import com.amazonaws.xray.sql.TracingDataSource;
import nablarch.core.log.Logger;
import nablarch.core.log.LoggerManager;
import nablarch.core.repository.di.ComponentFactory;

import javax.sql.DataSource;

public class TracingDataSourceFactory implements ComponentFactory<DataSource> {
    /** ロガー */
    private static final Logger LOGGER = LoggerManager.get(TracingDataSourceFactory.class);
    /** データソース */
    private DataSource dataSource;

    @Override
    public DataSource createObject() {
        LOGGER.logInfo("Wrap " + dataSource + " in " + TracingDataSource.class.getName());
        return TracingDataSource.decorate(dataSource);
    }

    /**
    * データソースを設定する。
    *
    * @param dataSource データソース
    */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

アーキタイプから生成したプロジェクトではデータソースの設定が src/main/resources/data-source.xml に記述されている。 これを以下のように編集する。

  • dataSource という名前で com.zaxxer.hikari.HikariDataSource が定義されているので、 rawDataSource という名前に変更する。
  • 代わりに上記で作成した TracingDataSourceFactorydataSource という名前で定義する。
  • TracingDataSourceFactory には元になるデータソースをプロパティとして設定する必要がある。元になるデータソースには、 rawDataSource を設定する。

Nablarchは dataSource という名前でデータソースコンポーネントを取得する。 このように編集することでX-Ray SDK for Java JDBCインターセプターがデータソース設定に追加され、SQLクエリが計測されるようになる。

<component name="rawDataSource"
           class="com.zaxxer.hikari.HikariDataSource" autowireType="None">
  <property name="driverClassName" value="${nablarch.db.jdbcDriver}"/>
  <property name="jdbcUrl" value="${nablarch.db.url}"/>
  <property name="username" value="${nablarch.db.user}"/>
  <property name="password" value="${nablarch.db.password}"/>
  <property name="maximumPoolSize" value="${nablarch.db.maxPoolSize}"/>
  <property name="minimumIdle" value="${nablarch.db.minimumIdle}"/>
  <property name="connectionTimeout" value="${nablarch.db.connectionTimeout}"/>
  <property name="idleTimeout" value="${nablarch.db.idleTimeout}"/>
  <property name="maxLifetime" value="${nablarch.db.maxLifetime}"/>
  <property name="validationTimeout" value="${nablarch.db.validationTimeout}"/>
</component>
<component name="dataSource" class="com.example.system.awsxray.TracingDataSourceFactory">
  <property name="dataSource" ref="rawDataSource" />
</component>