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);
}
}
ComponentFactory
を src/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
という名前に変更する。- 代わりに上記で作成した
TracingDataSourceFactory
をdataSource
という名前で定義する。 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>