CLOVER🍀

That was when it all began.

JavaのDNSキャッシュの有効期限を設定・確認する

これは、なにをしたくて書いたもの?

  • JavaDNSキャッシュについては、有効期限を無制限にしていてトラブるみたいは話は聞いたことがあった程度
  • あんまり意識する機会がなかったので、設定方法とその確認方法を見ておきたいなと

というわけで、ちょっとサンプルを書きつつ試してみたいと思います。

参考にしたもの

そもそもは、このあたりの話から、「そうなのかー」と思ったのがきっかけ。

DNS 名参照用の JVM TTL の設定 - AWS SDK for Java

Apache TomcatからELBにアクセスする際に気をつけたい事 sun.net.inetaddr.ttl=-1 | DevelopersIO

知ったのはそれなりに前なのですが、自分で設定を確認しよう、と思ったのが今になります。

Java プログラムでホスト名の名前解決ができなくなる原因。JVM の DNS キャッシュの仕組みと、キャッシュの有効期間を変更する方法 - Qiita

JVM の DNS キャッシュを制御する - 平常運転

設定方法

Javaのドキュメントでいくと、ここに設定するためのプロパティが書かれています。

ネットワークのプロパティ / Java 8

ネットワークのプロパティ / Java 10

設定できるのは、次の2つです。

  • 名前解決に成功した結果のキャッシュ期間(秒単位)
  • 名前解決に失敗した結果のキャッシュ期間(秒単位)

Java 8までは、設定方法は次の2つです。

  • セキュリティプロパティ(Security.setPropertyまたは$JAVA_HOME/lib/security/java.security)
  • システムプロパティ

の2つがありますが、後者よりも前者のセキュリティプロパティを使用する方が推奨されています。

Java 9および10のドキュメント上からは、すでにシステムプロパティの記載はなくなっています。

デフォルト値は…

networkaddress.cache.ttl
java.securityで指定して、ネーム・サービスからの名前の検索に成功した場合のキャッシング・ポリシーを示します。指定する値は、成功した検索結果をキャッシュする秒数を示す整数です。-1の値は、「ずっとキャッシュする」という意味です。デフォルトでは、セキュリティ・マネージャがインストールされている場合はずっとキャッシュし、セキュリティ・マネージャがインストールされていない場合は実装固有の期間キャッシュします。

networkaddress.cache.negative.ttl (デフォルト: 10)
java.securityで指定して、ネーム・サービスからの名前の検索に失敗した場合のキャッシング・ポリシーを示します。指定する値は、失敗した検索結果をキャッシュする秒数を示す整数です。0の値は、「キャッシュしない」という意味です。-1の値は、「ずっとキャッシュする」という意味です。

だそうです。名前解決に成功した場合のキャッシュの有効期限は、セキュリティ・マネージャーの有無で結果が変わるようですね。

今回は、Java 8で確認します。

環境

確認した環境は、こちらです。

$ java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-0ubuntu0.18.04.1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

セキュリティプロパティで設定する

最初に、正攻法であるセキュリティプロパティで指定する方法から。

最初に、現状の設定を確認してみましょう。 PrintDnsCacheTtl.java

import sun.net.InetAddressCachePolicy;

public class PrintDnsCacheTtl {
    public static void main(String... args) {
        System.out.printf("cache ttl = %d%n", InetAddressCachePolicy.get());
        System.out.printf("negative cache ttl = %d%n", InetAddressCachePolicy.getNegative());
    }
}

どうも、DNSのキャッシュの設定が定義してあるのは、このsun.net.InetAddressCachePolicyクラスのようなので、 こちらで確認します。

コンパイル&実行。

$ javac PrintDnsCacheTtl.java

$ java PrintDnsCacheTtl 
cache ttl = 30
negative cache ttl = 10

名前解決に成功した場合のキャッシュが30秒、失敗した場合のキャッシュが10秒です。

では、security.propertiesを変更してみます。

$ sudo vim $JAVA_HOME/jre/lib/security/java.security

もともと、次の1行が設定されていたので、

networkaddress.cache.negative.ttl=10

今回は、こんな感じの設定に変更。

networkaddress.cache.ttl=60
networkaddress.cache.negative.ttl=45

確認。

$ java PrintDnsCacheTtl
cache ttl = 60
negative cache ttl = 45

変更が反映されましたね。

ただ、この方法だとこの設定ファイルを使用するJavaアプリケーションが全部変更されます、と。

security.propertiesは、1度もとに戻します。

対象のアプリケーションを絞る場合は、Security.setPropertyで指定します。

ソースコード。 ApplySecurityPropertyDnsCacheTtl.java

import java.security.Security;

import sun.net.InetAddressCachePolicy;

public class ApplySecurityPropertyDnsCacheTtl {
    public static void main(String... args) {
        Security.setProperty("networkaddress.cache.ttl", "60");
        Security.setProperty("networkaddress.cache.negative.ttl", "45");

        System.out.printf("cache ttl = %d%n", InetAddressCachePolicy.get());
        System.out.printf("negative cache ttl = %d%n", InetAddressCachePolicy.getNegative());
    }
}

コンパイル&確認。

$ javac ApplySecurityPropertyDnsCacheTtl.java

$ java ApplySecurityPropertyDnsCacheTtl      
cache ttl = 60
negative cache ttl = 45

変更できました、と。

この方法の場合は、Security.setPropertyを使うため、ソースコード(アプリケーション)でなんとかする必要があるという
話になります。

システムプロパティを使用する場合

続いて、推奨はされていませんがシステムプロパティを使う場合。

先程のプログラムを使用して、確認してみます。

$ java -Dsun.net.inetaddr.ttl=60 -Dsun.net.inetaddr.negative.ttl=45 PrintDnsCacheTtl
cache ttl = 60
negative cache ttl = 10

negative ttlの方が、変わっていません…。

これは、security.propertiesのデフォルト値が、10だったからです。

ここで、security.propertiesの定義値をコメントアウトすると

#networkaddress.cache.negative.ttl=10

反映されるようになります。

$ java -Dsun.net.inetaddr.ttl=60 -Dsun.net.inetaddr.negative.ttl=45 PrintDnsCacheTtl
cache ttl = 60
negative cache ttl = 45

セキュリティプロパティとシステムプロパティを同時に指定したら?

先程の例でお気づきかもしれませんが、セキュリティプロパティとシステムプロパティを同時に指定すると、
セキュリティプロパティが優先されます。

これは、先にセキュリティプロパティを取得し、設定されていなければシステムプロパティから取得するように
なっているためです。
※というか、フォールバック扱い

http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/31bc1a681b51/src/share/classes/sun/net/InetAddressCachePolicy.java#l92

http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/31bc1a681b51/src/share/classes/sun/net/InetAddressCachePolicy.java#l130

これらの処理は、staticイニシャライザで行われるため、プログラム中で設定する場合はこのクラスが利用される前
(要するに、ネットワークアクセスに関する処理を行う前)に有効期限を設定する必要がある、ということになります。

なお、デフォルトでセキュリティマネージャーがインストールされていない場合はずっとキャッシュするというのは
このあたりの話で

http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/31bc1a681b51/src/share/classes/sun/net/InetAddressCachePolicy.java#l59

インストールされている場合は実装固有の値が使われる、というのはこのあたりですね。

http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/31bc1a681b51/src/share/classes/sun/net/InetAddressCachePolicy.java#l123

Java 10では?

ところで、すでにドキュメントからは記載はなくなっていますが、実際のところ、どうなんでしょうね?

ソースコードを見てみました。

http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/classes/sun/net/InetAddressCachePolicy.java

まだ残ってはいるみたいですね。

とはいえ、ドキュメントからはなくなっているので、そのうちホントになくなるんでしょうねぇ。