다음과 같은 상황이 있다고 생각해보자.
A 서비스에 100명의 사용자가 요청을 보내면 톰캣은 A 서비스의 요청을 처리하기 위해 B서비스를 호출한다.
10초 후 새로운 100명의 사용자가 요청을 보낸다. 하지만 B 서비스의 요청에 응답이 오지 않았기 때문에 대기는 끝나지 않는다. 이렇게 서비스가 마비된다.
→ 연동 서비스에 대한 타임아웃을 설정하지 않으면 연동 서비스의 응답이 느려질 때에 처리량이 급격히 떨어진다.
타임아웃을 설정하여 사용자에게 지정 시간 이후 에러 화면을 보게 하자. 반응 없는 무한 대기 화면보다는 에러 화면이라돋 보는 게 낫다. 또한 서비스의 자원이 포화되기 전에 응답하게 되므로 연동 서비스 문제가 다른 기능에 주는 영향도 줄어든다.
연결 타임아웃(Connection Timeout)
커넥션을 요청할때에 커넥션 수락을 얼마나 기다릴지 기다리는 시간
읽기 타임아웃(Read Timeout)
커넥션이 되고 응답을 받기까지의 시간
처음 연동하는 서비스라면 보통 이렇게 타임아웃을 설정해둔다.
소켓 타임아웃과 읽기 타임아웃
- 읽기 타임아웃을 지정할 때에는 실제로 설정되는 값이 무엇인지 보고 결정해야함.
- 아파치의 HTTPClient 의 경우 소켓 타임아웃을 설정함
- 소켓 타임아웃은 네트워크 패킷 단위를 기준으로 하므로 전체 응답 시간에 대한 타임아웃을 의미지는 않는다.
- OkHttp 는 읽기 타임아웃과는 별개로 호출 타임아웃을 설정 가능
- 요청 시작부터 응답까지의 전체 시간 기준으로 설정
- 소켓 타임아웃을 설정하면 패킷은 계속 수신되지만 전체 처리 시간이 오래 걸리는 경우 타임아웃 발생시킬 수 잇음.
OkHttp란?
OkHttp는 HTTP 및 HTTP/2 클라이언트 라이브러리로, 안드로이드 및 자바 애플리케이션에서 네트워크 요청을 처리하는 데 널리 사용되는 오픈소스 라이브러리입니다. Square사에서 개발했으며, 효율적인 연결 관리와 다양한 타임아웃 설정 옵션을 제공합니다.
"소켓 타임아웃은 네트워크 패킷 단위를 기준으로 하므로 전체 응답 시간에 대한 타임아웃을 의미하지는 않는다"의 의미
이 문장은 소켓 타임아웃의 작동 방식을 설명하는 중요한 개념입니다:
- 소켓 타임아웃의 특성: 소켓 타임아웃은 각 네트워크 패킷이 도착하는 간격을 측정합니다. 즉, 패킷과 패킷 사이의 대기 시간을 기준으로 합니다.
- 전체 응답 시간과의 차이: 만약 서버가 데이터를 천천히 보내더라도, 패킷이 계속해서 도착하기만 한다면 타임아웃이 발생하지 않습니다. 예를 들어, 소켓 타임아웃을 10초로 설정했다고 가정해봅시다. 서버가 총 5분 동안 응답을 보내는데, 매 5초마다 패킷을 보낸다면 타임아웃이 발생하지 않습니다. 전체 응답 시간은 5분이지만, 패킷 간 간격은 10초 미만이기 때문입니다.
- 문제점: 이로 인해 전체 처리 시간이 매우 길어질 수 있습니다. 클라이언트는 서버로부터 느리게 전송되는 데이터를 계속 기다리게 되어, 의도하지 않게 매우 긴 시간 동안 연결이 유지될 수 있습니다.
OkHttp의 해결책
OkHttp는 이러한 문제를 해결하기 위해 읽기 타임아웃과는 별개로 "호출 타임아웃(Call Timeout)"을 제공합니다. 호출 타임아웃은 요청 시작부터 응답 완료까지의 전체 시간을 기준으로 작동하므로, 서버가 아무리 패킷을 계속 보내더라도 전체 처리 시간이 설정된 시간을 초과하면 타임아웃이 발생합니다.
벌크 헤드 패턴
동시요청을 제한하는 방식은 벌크페드 패턴이라고 한다. 벌크헤드 패턴은 각 구성요소를 격리하여 한 구성요소의 장애가 다른 구성요소에 영향을 주지 않도록 ㅅ하는 설계 패턴이다.
앞서 연동 서비스에 시간이 걸릴 경우에는 타임아웃을 설정하여 지정 시간 이상 걸릴 경우에는 에러 화면이 보여지는 방법을 설명했다.
하지만 연동 서비스가 정상 상태가 아님을 알 수 있다면 연동 서비스에 요청을 보내지 않고 바로 에러를 응답하는 것이 낫다.
(왜냐하면) 서버 입장에서는 읽기 타임아웃 시간동안 발생하는 응답시간을 줄일 수 있고 처리량도 감소시킬 수 있고 사용자 입장에서는 몇초 기다리는 것 보다 바로 에러 화면을 보는 것 보다 바로 에러화면을 보는 것이 낫기 때문이다.
서킷 브레이커가 작동하는 방식이 이와 같다.
서킷 브레이커는 누전 차단기처럼 과도한 트래픽이 발생하면 연동을 중지시키고 바로 에러를 응답한다.
서킷 브레이커는 닫힘, 열림, 반 열림 의 3가지 상태를 갖는다.
예시 1. 외부 연동에 실패했을 때 트랜잭션을 롤백
예시 2. 외부 연동은 성공했지만 DB 연동에 실패해 트랜잭션을 롤백
외부 연동은 성공했지만 DB 연동에 실패된 경우에는 취소 API를 호출해서 외부 연동을 이전 상태로 되돌리는 것이 필요하다.
HTTP 커넥션 풀 크기
풀의 크기는 연동할 서비스의 성능에 따라 결정한다. 커넥션풀 크기를 무작정 늘리면 연동 서비스의 성능 저하가 우리 서비스 전체의 응답시간에 영향을 끼친다. 그렇기에 반드시 연동 서비스의 처리 능력을 고려하자
풀에서 HTTP 커넥션을 가져올 때까지 대기하는 시간
커넥션 풀의 크기가 10개라면 11개의 외부 연동요청이 들어온 경우 10개는 커넥션을 확보해 실행되고 나머지 1개는 대기하게 된다. 대기 시간은 수 초 이내의 짧은 시간으로 설정하는 것이 좋다.
HTTP 커넥션 유지 시간
끊어진 커넥션을 사용하면 에러가 발생하ㅡ로 연동 서비스에 맞춰 유지 시간을 적절히 설정하자.