JVM は ホスト名を解決すると IP アドレスをキャッシュします。
発生する問題
あるサーバのホスト名が変化しなくても、IP アドレスが変わると、 Java のプログラムからそのサーバにアクセスできなくなることがあります。
具体的には、 AWS の ELB はホスト名が変わらずに IP アドレスだけ勝手に変化することがあるので、そのような場合に、名前解決に失敗します。
もちろんサーバ側の管理者が DNS の A レコードを変更したときにも発生するので、 AWS のみならず一般的に起き得ます。
どのようにキャッシュしている?
キャッシュを保持してある場所
InetAddress クラスがキャッシュを保持しています。
キャッシュが存在する限り、名前解決をしようとしても、キャッシュが参照されます。
キャッシュの有効期間を決定する場所
InetAddressCachePolicy クラスがキャッシュの有効期間を決定しています。
キャッシュの有効期間には 2 種類あります。
- 成功した名前解決の結果のキャッシュの有効期間
- 失敗した名前解決の結果のキャッシュの有効期間
どちらもセキュリティプロパティで設定します。
セキュリティプロパティというのは、後述しますが、 $JAVA_HOME/lib/security/java.security というファイルに記述したりして設定できます。
セキュリティプロパティが読み込まれるのは、InetAddressCachePolicy のソースを見ると、 static 初期化子のなかです。
なので InetAddressCachePolicy クラスの static 初期化子が実行される前にセキュリティプロパティを指定すると、DNS キャッシュの有効期間を変更できます。
具体的には、ネットワークを使用するクラス全般を呼び出すと、InetAddressCachePolicy クラスもロードされて static 初期化子が実行されるので、ネットワークが関連してくるようなコードを実行するよりも先に、セキュリティプロパティを指定する必要があります。
セキュリティプロパティを指定する方法
3 つの方法があります。
$JAVA_HOME/lib/security/java.security に記述
- 成功した名前解決の結果のキャッシュの有効期間
networkaddress.cache.ttl=10
- 失敗した名前解決の結果のキャッシュの有効期間
networkaddress.cache.negative.ttl=10
単位は秒です。
値が -1 なら有効期間は無制限です。
値が 0 ならキャッシュは無効です。
java.security.Security.setProperty() で指定
コードで指定する方法です。
- 成功した名前解決の結果のキャッシュの有効期間
Security.setProperty("networkaddress.cache.ttl", "0");
- 失敗した名前解決の結果のキャッシュの有効期間
Security.setProperty("networkaddress.cache.negative.ttl", "0");
前述した通り、 InetAddressCachePolicy クラスがロードされるよりも先に Security.setProperty() を実行する必要があります。
たとえば Log4j のロガーのインスタンスを作成すると、間接的に InetAddressCachePolicy クラスもロードしてしまうので、それよりも先に実行しなければいけません。
main メソッドがあるクラスの先頭に下のように書けば確実だとおもいます。
static {
Security.setProperty("networkaddress.cache.ttl", "0");
Security.setProperty("networkaddress.cache.negative.ttl", "0");
}
$JAVA_HOME/lib/security/java.security に記述する場合と同じく、
単位は秒です。
値が -1 なら有効期間は無制限です。
値が 0 ならキャッシュは無効です。
起動オプションに指定
仕様 によれば将来的に使えなくなるかもしれない方法です。
java -Dsun.net.inetaddr.ttl=0 -Dsun.net.inetaddr.negative.ttl=0 (後略)
と指定します。
これも単位は秒で、 -1 は無制限、 0 はキャッシュ無効です
# 参考
- InetAddress: InetAddress クラスの API 仕様。「InetAddressのキャッシュ」の章にキャッシュについての説明があります。
- Apache TomcatからELBにアクセスする際に気をつけたい事 sun.net.inetaddr.ttl=-1: Tomcat + ELB の組み合わせでこの問題が発生した事例を紹介されています。Tomcat を使用している場合の セキュリティプロパティの設定方法を紹介されています。
- Java/Socket, InetAddressにおけるDNS名前解決の仕組みと networkaddress.cache.ttl: キャッシュの仕組みをコードのレベルで詳しく紹介されています。私がググった中では最も詳しいです。