软件系统对运行在不同进程或者网路中不同的机器的软件进行远程调用是很常见的。内存中调用和远程调用之间的一个主要区别是,远程调用可能会失败,或者在达到某个超时限制之前挂起而没有响应。更糟糕的是,如果一个响应延迟的服务提供方上有许多调用者,那么您可能会耗尽关键资源,导致跨多个系统的连锁故障。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