軟件系統對運行在不同進程或者網路中不同的機器的軟件進行遠程調用是很常見的。內存中調用和遠程調用之間的一個主要區別是,遠程調用可能會失敗,或者在達到某個超時限制之前掛起而沒有響應。更糟糕的是,如果一個響應延遲的服務提供方上有許多調用者,那么您可能會耗盡關鍵資源,導致跨多個系統的連鎖故障。Michael Nygard在他的優秀著作《發布》中推廣了斷路器模式,以防止這種災難性的連鎖故障。
斷路器的基本原理很簡單。您將一個受保護的函數調用封裝在一個斷路器對象中,該斷路器對象監視故障。一旦故障達到某個閾值,斷路器就會跳閘,所有對斷路器的繼續調用都會返回一個錯誤,受保護的調用也不會繼續。如果斷路器跳閘,您通常還需要通過監視器進行警報。
下面是Ruby寫的一個簡單示例,用于防止超時。
我使用block (Lambda)設置了斷路器,它是受保護的調用。
cb = CircuitBreaker.new {|arg| @supplier.func arg}
斷路器存儲block,初始化各種參數(閾值、超時和監視功能),并將斷路器重置為關閉狀態。
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
如果線路關閉,則調用斷路器將調用底層block,如果打開則返回錯誤
# client code
aCircuitBreaker.call(5)
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 record_failure
@failure_count += 1
@monitor.alert(:open_circuit) if :open == state
end
def reset
@failure_count = 0
@monitor.alert :reset_circuit
end
將故障失敗數與閾值進行比較,確定斷路器的狀態
class CircuitBreaker...
def state
(@failure_count >= @failure_threshold) ? :open : :closed
end