본문 바로가기
BackEnd/이슈 정리

[spring] 간헐적으로 응답 지연 되는 이슈

by 하용권 2025. 9. 2.

회사에서 겪은 일입니다.

 

1. 현상

 

모니터링 툴을 이용해서 보면, 가끔가다가 5초 이상 지연되는 건이 가끔 있었습니다.

 

하지만 최근에 20초 이상 걸리는 건도 갑자기 많아졌고, 해당 시간대에 특정 서버의 tps가 감소했다가 갑자기 증가하는 경우도 있었습니다. 

 

그리고 tps 가 증가할 때, cpu 사용량도 증가했었습니다.

 

무엇보다 클라이언트에서 timeout으로 인해 데이터를 제대로 가져가지 못하는 현상이 발생합니다.

 

 

2. 원인

APM 을 이용해서 모니터링을 진행하고 있습니다.

그렇다 보니, redis를 조회하거나 db를 조회하는 로직에서 몇 초 정도 걸렸는지 알 수 있습니다.

 

처음에 이를 봤을 때는 어디서 문제가 발생하는지 몰랐었습니다...

 

그래서 지연된 범위 내에서 copilot에게 성능 이슈가 있을 만한 곳을 찾아달라고 했었고, 

여러 후보 중, Inetaddress.getlocalhost().gethostname() 가 유력해 보였습니다.

 

 

이는 로깅을 할 때, 어느 서버에서 처리를 했는지 알기 위해 이를 호출하고 있었습니다.

 

그래서 이에 대해서 조사해 보았습니다.

 

3. Inetaddress.getlocalhost().gethostname() 로직 분석

 

처음에는

https://pkgonan.github.io/2018/06/InetAddress-getLocalHost

 

InetAddress 클래스 사용으로 인한 성능 이슈, 나아가 AWS EC2 환경에서의 동작 방식 분석

경험과 기억을 공유하다

pkgonan.github.io

를 보고 이슈가 있다는 것을 알았습니다.

 

하지만 해당 내용은 옛날 내용으로 보입니다.

그래서 직접 소스를 분석해 봤습니다.

 

이는 jdk 17에 관한 코드입니다.

String local이라는 변수를 가져옵니다.

 

이는 

 

 

https://github.com/openjdk/jdk/blob/jdk-17%2B0/src/java.base/unix/native/libnet/Inet4AddressImpl.c#L73-L84

 

여기서 gethostname은 시스템 콜을 이용해서 가져오게 됩니다.

이는 /etc/hostsname 파일에서 가져옵니다. 만약 존재하지 않는다면, localhost를 반환하게 됩니다.

 

아까 보던 java 파일로 다시 돌아와서,

localhost를 반환하면, native 코드의 loopbackAddress() 메소드가 호출이 됩니다. 이는 루프백 주소(127.0.0.1)을 가져옵니다.

 

하지만 문제가 발생했던 서버의 hostname을 호출해 보니, localhost가 아니었습니다.

즉, 해당 호스트에 해당하는 ip 주소를 가져올 필요가 있습니다.

 

 

이런 메소드를 이용해서 ip 주소를 가져오게 됩니다.

 

 

lookupAllHostAddr 메소드를 통해서 ip 주소를 가져옵니다.

 

nameService는 디버깅을 해보니,PlatformNameService라는 클래스에서 호출합니다. 

그리고 Inet4Address의 native method를 호출하게 됩니다.

 

이 부분은 도저히 모르겠어서, gpt에게 물어봤습니다...

 

 

이는 /etc/nsswitch.conf 파일에 정의되어 있는 대로 호출을 하게 됩니다.

 

만약 files dns 순서로 정의되어 있다면,

1. /etc/hosts 파일에서 조회

2. /etc/resolve.conf 를 보고 dns 서버에 요청

 

이 코드들은 glibc에 구현되어 있다고 합니다.

 

문제가 발생했던 서버에서는 /etc/hosts에 자신의 호스트가 어떤 ip를 가지고 있는지 정의가 되어 있지 않았습니다. 그래서 2번으로 가게 되었고, dns를 조회하는 과정에서 네트워크 문제가 있어서 조회가 오래 걸린 것으로 파악합니다.

 

그래서 네트워크가 불안정한 시간대에는 지연이 발생했었습니다.

물론 캐싱이 되어 있지만, 캐싱 expire time이 설정되어 있어서 dns를 종종 조회하게 됩니다.

 

 

4. 해결

로깅을 할 때마다, 문제가 되는 메서드를 호출했었습니다.

 

대신, 서버 첫 기동 시에 캐싱해서 사용하도록 수정했습니다.

 

그리고 약 2달 정도 모니터링을 했는데, 지연되는 문제가 완전히 해결되었습니다!

 

 

5. 느낀 점

java 기본 메소드라서 사실 여기서 문제가 발생할 줄은 몰랐습니다.

 

문제 발생한 곳을 찾은 후, ai에 분석을 요청함으로써 바로 발견할 수 있었습니다. 만약 ai가 없었다면 이 문제를 해결할 수 있었을지 지 감도 안 잡히네요. 왜 글로벌 기업들이 개발자 인력을 감축하고 있는지 알 수 있었습니다. ai를 잘 활용하면 생산성이 엄청나게 증가하게 되네요.

 

 

그리고 기초 cs 지식도 많이 중요하다는 것을 알았습니다. host name을 가져오고, 주소를 가져오는 과정을 구체적으로 몰랐는데 이번 기회를 통해서 많이 알게 되었습니다. 이런 개선 포인트를 찾아서 공부하는 것이 재미있네요.

반응형