728x90
반응형
- 장애가 발생하는 서비스에 반복적인 호출이 되지 못하게 차단
- 특정 서비스가 정상적으로 동작하지 않을 경우 다른 기능으로 대체 수행하여 장애 회피
마틴 파울러는 루비를 이용해서 timeouts에 대해 보호하는 간단한 예를 보여준다.
아래 브레이커는 블럭을 저장하고, thresholds, timeouts, mornitoring과 같은 파라미터를 저장한다. 그리고 상태가 종료되면 리셋된다.
class CircuitBreaker...
attr_accessor :invocation_timeout, :failure_threshold, :monitor
def initialize &block
@circuit = block
@invocation_timeout = 0.01
@failure_threshold = 5
@monitor = acquire_monitor
reset
end
cb = CircuitBreaker.new {|arg| @supplier.func arg}
클라이언트의 코드는 아래와 같다.
# client code
aCircuitBreaker.call(5)
아래는 타임아웃 예제이다. 타임아웃이 발생하면 실패 카운터를 증가시키고, 성공하면 0으로 리셋시킨다.
class CircuitBreaker...
def call args
case state
when :closed
begin
do_call args
rescue Timeout::Error
record_failure
raise $!
end
when :open then raise CircuitBreaker::Open
else raise "Unreachable Code"
end
end
def do_call args
result = Timeout::timeout(@invocation_timeout) do
@circuit.call args
end
reset
return result
end
그리고 아래와 같이 상태를 정의한다. 실패 카운트가 스레스 홀드 값을 넘으면 오픈, 아니면 클로즈다.
class CircuitBreaker...
def state
(@failure_count >= @failure_threshold) ? :open : :closed
end
아래는 상태를 오픈과 하프 오픈으로 나타낸 서킷 브레이커의 흐름도이다. 실제 전자 회로에서의 Circuit Breaker 와 비슷하다.
블럭, 타임아웃, 스레스홀드, 모니터, 리셋 타임아웃 변수와 리셋 값을 가지고 있는 리셋 서킷 브레이커다.
class ResetCircuitBreaker...
def initialize &block
@circuit = block
@invocation_timeout = 0.01
@failure_threshold = 5
@monitor = BreakerMonitor.new
@reset_timeout = 0.1
reset
end
def reset
@failure_count = 0
@last_failure_time = nil
@monitor.alert :reset_circuit
end
그리고 아래 코드는 해당 서킷 브레이커의 상태를 나타낸다.
class ResetCircuitBreaker...
def state
case
when (@failure_count >= @failure_threshold) &&
(Time.now - @last_failure_time) > @reset_timeout
:half_open
when (@failure_count >= @failure_threshold)
:open
else
:closed
end
end
추가로 함수를 호출하고, 실패를 저장하는 로직이다.
class ResetCircuitBreaker...
def call args
case state
when :closed, :half_open
begin
do_call args
rescue Timeout::Error
record_failure
raise $!
end
when :open
raise CircuitBreaker::Open
else
raise "Unreachable"
end
end
def record_failure
@failure_count += 1
@last_failure_time = Time.now
@monitor.alert(:open_circuit) if :open == state
end
서킷 브레이커는 말그대로 전자회로에서의 Circuit Breaker와 같다. 한 마이크로서비스에서 다른 마이크로 서비스로 통신하는 과정을 지켜보다가 일정 스레스홀드를 넘으면 회로를 차단시킨 후 적절한 조치를 취하도록 만든다.
이와 같은 패턴을 이용하면 요청을 보내는 마이크로서비스에서 다른 마이크로서비스로 인해 500과 같은 에러를 겪어도 정상인 것처럼 바꿔서 200코드를 보내는 등의 조치를 할 수 있고 이는 서비스를 정상적인 것처럼 보이게 만들어 UX를 증가시킨다.
References:
728x90
반응형