[프로그래밍] 타임아웃

타임아웃

요청자가 작업 요청을 했으나 작업 수행 결과에 대한 응답이 예상보다 늦어지는 상황을 생각해보자. 작업을 수행하는 시스템(애플리케이션 또는 함수가 될 수도 있다)의 성능 저하가 원인이라면 해당 시스템이 반응성 및 확장성을 갖고 있을 경우 스케일 아웃(또는 스케일 업)을 통해 응답 속도를 원래의 수준으로 복구할 수 있을 것이다. 반면 예기치 못한 오류로 인해 서비스 불가능한 상태가 된 것이 원인이라면 정상화를 위해 별도의 조치를 취해야 하는 상황일 수도 있다. 이외에도 네트워크 상황이 좋지 않은 등의 원인이 있을 수 있겠다.

요청한 시스템이 어떤 응답을 보내는지 요청하는 쪽에서 미리 알고 있는 경우도 있지만 그렇지 않은 경우도 있을 것이다. 앞서 말한 시스템의 성능 저하로 인해 응답 속도가 느려진 경우 성능이 저하되었음을 별도의 응답 메시지나 응답 코드로 표현하여 전달하지 않는 이상 요청자는 무엇 때문에 응답이 오래 걸리는지 알 수 없다. 시스템의 서비스 불능 상태가 원인이면 요청자는 서버 상태(예: HTTP 500 에러)가 응답으로 오기를 기대할 수 있지만 그렇지 않은 상황이라면 마찬가지로 무엇 때문에 응답이 오래 걸리는지 알 수 없다.

요청자는 마냥 응답을 기다리고만 있을 수 없다. 요청에 대한 작업이 그 다음 작업 수행에 의존적인(동기적) 상황이거나 의존적이지 않지만(비동기적) 스레드를 차단하는 작업이라면 리소스가 낭비되지 않도록 별도의 장치가 필요하다. 타임아웃은 그 장치 중 하나이다. 요청자는 제한된 리소스를 효율적으로 이용해야 한다.

응답을 기대하는 작업이 아무런 응답을 보내지 않을 때를 대비하여 지연 시간(응답을 기다릴 수 있는 최대 허용 시간)을 설정한 후 해당 시간 내에 응답이 오지 않으면 타임아웃이라는 이벤트를 발생시켜 응답 대기를 중단시키고 이를 요청자가 인지하도록 한다. 타임아웃은 런타임 동안 발생하는 이벤트 및 에러이며 별도의 예외 처리가 가능할 것이다.

재시도

그렇다면 타임아웃이 발생했을 때 요청자가 어떤 일을 수행할 수 있을까. 시스템의 일시적인 에러일 수 있으므로 요청을 몇 번 더 보내볼 수 있다. 이러한 디자인 패턴을 재시도 패턴(retry pattern)이라고 한다. 재시도 패턴 및 전략은 네트워크 통신에서 오류 응답을 처리하는, 간단하면서 강력한 방법 중 하나이다. 하지만 무작정 재시도를 하는 것은 요청을 하는 시스템과 요청을 받는 시스템 모두에게 부하를 줄 수 있으므로 추가적인 전략이 필요하다.

요청자는 재시도 횟수나 재시도 간격(백오프, backoff) 설정, 지수 백오프(exponential backoff)를 이용한 점진적 대기 시간 증가 설정 등을 통해 재시도 전략을 상황에 따라 다르게 가져갈 수 있다. 재시도를 해보았음에도 여전히 응답이 오지 않아 타임아웃이 발생하면 해당 요청에 대한 응답이 오지 않았을 때 할 수 있는 작업을 수행한다.

서킷 브레이커

시스템의 일시적인 실패일 경우 요청을 여러 번 다시 보내는 시도를 해볼 수 있다. 그러나 실패한 시스템 복구가 예상보다 길어지는 상황에서, 성공적으로 응답할 가능성이 없는 작업에 대한 요청을 지속적으로 재시도하는 것은 무의미하다. 실패한 시스템에 대한 요청을 지속적으로 시도한다면 요청 대상 시스템에 악영향을 미칠 수도 있으며 요청을 하는 시스템도 재시도를 하는 동안 시간과 리소스 낭비를 겪게 된다. 따라서 요청자는 시도하려는 작업이 실패함을 빠르게 인지하고 이를 차선책으로 처리하는 것이 낫다.

여러 서비스가 연결되어 하나의 시스템을 구성하는 마이크로서비스의 경우 시스템의 한 부분에서 실패가 발생하면 연쇄적 실패로 이어져 시스템 전체가 실패 상태가 될 수 있다.

실패할 가능성이 있는 작업 수행을 막아주는 디자인 패턴을 서킷 브레이커 패턴(circuit breaker pattern)이라고 한다. 이 패턴을 통해 오류가 일시적이지 않다는 것을 요청자에게 알려서 재시도를 포기하도록 만든다. 또한 시스템의 연쇄적 실패로 이어지기 전에, 실패하게 될 작업을 요청하지 않도록 한다.

원래 요청하고자 했던, 실패한 작업 대신 요청할 작업을 폴백(fallback)이라고 하며 폴백을 수행함으로써 요청자는 실패 확산을 방지할 수 있다. 서킷 브레이커 패턴에서는 폴백을 실행하는 상태(열림 상태)가 된 이후에도 기존 요청이 여전히 실패 중인지, 다시 정상 상태로 돌아왔는지 확인한 후 정상으로 확인되면 폴백 대신 기존 요청을 다시 시도한다(닫힘 상태).

Comments