本文摘要:软件系统对运营在有所不同进程或者网路中有所不同的机器的软件展开远程调用是很少见的。
软件系统对运营在有所不同进程或者网路中有所不同的机器的软件展开远程调用是很少见的。内存中调用和远程调用之间的一个主要区别是,远程调用可能会告终,或者在超过某个超时容许之前悬挂而没有响应。更加差劲的是,如果一个号召延后的服务获取方上有许多调用者,那么您可能会消耗关键资源,造成横跨多个系统的连锁故障。
MichaelNygard在他的杰出著作《公布》中推展了断路器模式,以避免这种灾难性的连锁故障。断路器的基本原理很非常简单。
您将一个受保护的函数调用PCB在一个断路器对象中,该断路器对象监控故障。一旦故障超过某个阈值,断路器就不会跳闸,所有对断路器的之后调用都会回到一个错误,受保护的调用也会之后。如果断路器跳闸,您一般来说还必须通过监视器展开警报。
下面是Ruby写出的一个非常简单示例,用作避免超时。我用于block(Lambda)设置了断路器,它是受保护的调用。cb=CircuitBreaker.new{|arg|@supplier.funcarg}断路器存储block,初始化各种参数(阈值、超时和监控功能),并将断路器重置为重开状态。
classCircuitBreaker...attr_accessor:invocation_timeout,:failure_threshold,:monitordefinitialize&block@circuit=block@invocation_timeout=0.01@failure_threshold=5@monitor=acquire_monitorresetend如果线路重开,则调用断路器将调用底层block,如果关上则回到错误#clientcodeaCircuitBreaker.call(5)classCircuitBreaker...defcallargscasestatewhen:closedbegindo_callargsrescueTimeout::Errorrecord_failureraise$!endwhen:openthenraiseCircuitBreaker::Openelseraise"UnreachableCode"endenddefdo_callargsresult=Timeout::timeout(@invocation_timeout)do@circuit.callargsendresetreturnresultend如果我们调用超时,我们故障计数器计数减少,调用顺利则将其重置为零。classCircuitBreaker...defrecord_failure@failure_count+=1@monitor.alert(:open_circuit)if:open==stateenddefreset@failure_count=0@monitor.alert:reset_circuitend将故障告终数与阈值展开较为,确认断路器的状态classCircuitBreaker...defstate(@failure_count>=@failure_threshold)?:open::closedend这个非常简单的断路器防止了在电路关上时展开调用,但是当一切恢复正常时必须外部介入来重置它。对于建筑物中的断路器,这是一种合理的方法,但是对于软件中断路器,我们可以让断路器本身检测底层调用否可以之后。
我们可以通过在必要的间隔之后再度尝试被维护调用来构建这种可调改置不道德,顺利时则重置断路器。创立这种断路器意味著必须为重置尝试加到一个阈值,并设置一个变量来留存上次错误时间。classResetCircuitBreaker...definitialize&block@circuit=block@invocation_timeout=0.01@failure_threshold=5@monitor=BreakerMonitor.new@reset_timeout=0.1resetenddefreset@failure_count=0@last_failure_time=nil@monitor.alert:reset_circuitend现在经常出现了第三种状态—半对外开放状态—这意味著线路早已准备好展开试验性的现实调用,想到问题否早已修缮。
classResetCircuitBreaker...defstatecasewhen(@failure_count>=@failure_threshold)&&(Time.now-@last_failure_time)>@reset_timeout:half_openwhen(@failure_count>=@failure_threshold):openelse:closedendend在半关上状态下的试验性调用,如果顺利,将重置断路器;如果告终,将重新启动超时设置。classResetCircuitBreaker...defcallargscasestatewhen:closed,:half_openbegindo_callargsrescueTimeout::Errorrecord_failureraise$!endwhen:openraiseCircuitBreaker::Openelseraise"Unreachable"endenddefrecord_failure@failure_count+=1@last_failure_time=Time.now@monitor.alert(:open_circuit)if:open==stateend这个例子很非常简单,在实践中断路器获取了更加多的特性和参数化设置。它们一般来说不会避免受保护调用有可能引起的一系列错误,比如网络连接告终。
并不是所有的错误都应当跳闸,有些是体现长时间的故障,必须作为常规逻辑的一部分展开处置。由于流量相当大,您可能会遇上大量调用等候超时的问题。
由于远程调用一般来说极快,所以最差将每个调用放到有所不同的线程上,用于future或promise来处置回到的结果。从线程池中萃取这些线程,在线程池消耗时决定线路插入。这个例子展出了一种非常简单的方法来跳闸—在顺利调用时重置计数。
一种更加简单的方法有可能是查阅错误的频率,比如,一旦超过50%的失败率,就不会跳闸。您还可以为有所不同的错误设置有所不同的阈值,例如超时阈值为10,相连告终阈值为3。我所展出的示例是用作实时调用的断路器,但是断路器对于异步通信也很简单。这里的一种少见技术是将所有催促放到一个队列中,服务提供者以一定速度消费该队列—这是一种防止服务器短路的简单技术。
在这种情况下,当队列被填充时,线路就不会插入。就其本身而言,断路器有助增加在有可能告终的操作者中闲置资源。
您可以防止客户端的超时等候,而插入的线路也可以防止给正处于困境的服务器减少阻抗。我在这里辩论的是远程调用,这是用于断路器的少见情况,但是它们可以用作任何必须维护系统部件免遭其他部件故障影响的情况。断路器是一个有价值的监测点。断路器中状态的任何变更都应当被记录,断路器应当表明其状态的详细信息,以便展开更加了解的监控。
断路器的不道德一般来说是一个很好的来源,来警告环境中更加深层次的问题。操作者人员应当需要跳闸或废黜断路器。
断路器本身是有价值的,但用于断路器的客户端必须对断路器故障作出反应。与任何远程调用一样,您必须考虑到在再次发生故障时应当做到什么。它否不会使你正在展开的操作者告终,或者否还有其他的解决办法?比如信用卡许可可以放到队列中几天后处置;通过表明一些可以拒绝接受的旧数据来减轻无法提供某些数据的问题。
本文来源:必一运动·(B-Sports)官方网站-www.qdhnzc.com