1. 현상 최근에 검색 서버를 이전하면서 유예기간을 두어 옛 서버와 새 서버를 동시에 지원해두기로 결정했다. 그런데 DNS에 두 IP를 Round-Robin 방식으로 요청할 때마다 다른 IP를 줄 수 있도록 변경했는데, 실제 JVM의 서비스에서는 아무리 해도 새 서버 IP으로 요청을 하지 않는 것이 었다.
2. 첫 시도 일단 캐시를 의심을 했다. 이런 경우 실정보 반영이 느린 캐시가 범인인 경우가 많은데, 여러가지 캐시들을 차근차근 정리했다.
3. JVM JVM에서는 Host Name resolutions에 caching을 지원하고 있다. 캐시 방법에는 두가지 방식이 있는데 positive, negative로 각각 성공했을 경우와 실패했을 경우 캐시 유지 정책을 뜻한다. 이 값을 설정하는 방법에는 두가지가 있는데,
$JAVA_HOME/jre/lib/security/java.security 에서 설정값을 넣어주는 경우
java.security.Security.setProperty("key","value")으로 값을 넣어주는 경우
이며 그 키와 밸류 값은 다음과 같다.
networkaddress.cache.ttl : name lookup이 성공했을 경우 갱신할 초, 기본값 -1으로 영원한 캐시
networkaddress.cache.negative.ttl : 실패했을 경우 갱신할 초, 기본값 10초
설정값을 변경하고 Tomcat을 종료, 시작 했다.
4. 현상 지속 현상은 지속되고 있었다. HttpClient 라이브러리를 사용하고 있었기 때문에 도대체 어떤일이 벌어지고 있는지 알 필요성을 느꼈다. 마침 사내에서 JProbe 8.0을 구매해두었기 때문에 설치에 들어갔다. 한참은 보다가 문득 JVM단위에서 문제가 있을 수도 있다는 생각이 들었다. 예전에 CVM(cdc)이라는 Sun에서 나온 J2ME 구현체를 포팅할 때 Multicast관련 설정 문제로 한참 고생하다 결국 CVM의 버그인 것을 밝혔던 경험이 영향을 미친 것 같다.
5. 문제 발견 먼저 가장 손쉽게 JVM 어플리케이션을 띄울 수 있는 방법은 Groovy라고 판단이 들었다. 그루비 설치 후 간단한 스크립트로 확인했다.
6. 문제 검증 혹시 나만 이런 현상을 느끼고 있는 것은 아닌지 구글링을 통해 다른 사람들의 아우성들을 보도록 했다. 특히 눈길 끈 문서는 톰캣 개발팀의 메일링 리스트 였다. networkaddress.cache.ttl이라든지 getByName이라든지 현상이라든지 유사점이 무척 많았다.
The multiple InetAddress.getByName() calls in the above for-loop all return the first IP returned from InetAddress.getAllByName() as though it is cached, even though the network.cache.ttl setting is clearly changed to "0".
하지만 Tomcat 4이라는 너무 오래된 버전에서의 이야기였다. 그리고 Tomcat에서의 문제라니. 그럼 그루비에서는 발생하지 않아야 하는 법이다.
7. 원인 getByName을 집중적으로 공략해보기로 했다. 일단 Java Doc에서는 아무런 언급이 없었다. 소스를 보아도 내부 필드를 바로 반환하는 방식으로 별 도움은 되지 못했다. Inet4Address 클래스도 보았다. 첫번째 의심 코드는 sun.net.util.IPAddressUtil.textToNumericFormatV4이었는데 소스를 보니 단순히 포맷팅과 관련있었다. 두번째 의심코드는 InetAddress.getAddressFromNameSevice이었는데 이부분은 모든 InetAddress를 반환하도록 되어 있었다.