1. 程式人生 > >Akka之Circuit Breaker

Akka之Circuit Breaker

 這周在專案中遇到了一個錯誤,就是Circuit Breaker time out。以前沒有接觸過,因此學習了下akka的斷路器。

一、為什麼使用Circuit Breaker

斷路器是為了防止分散式系統中的級聯故障,從而保障其穩定性。其應該與遠端系統之間介面的明智超時結合使用,以防止單個元件故障導致所有元件關閉。

例如,我們有一個與第三方遠端Web服務互動的應用程式。假設請求超出了第三方的容量,他們的資料庫在負載下不能正常工作了。假設資料庫以這樣的方式失敗,即將錯誤交還給第三方Web服務需要很長時間。這反過來使得請求在很長一段時間後失敗。回到我們的Web應用程式,使用者已經注意到他們的表單提交需要更長時間才能掛起。而使用者一般會不斷的使用重新整理按鈕,為他們已經執行的請求新增更多請求。這最終導致由於資源耗盡而導致Web應用程式失敗。這將影響所有使用者,即使那些未依賴於此第三方Web服務功能的使用者也是如此。

因此,在Web服務呼叫中引入斷路器將導致請求開始快速失敗,讓使用者知道出現問題並且他們不需要重新整理他們的請求。這也將故障行為限制為僅使用依賴於第三方功能的使用者,其他使用者不再受資源耗盡而受影響。

我們都知道在springcloud中hystrix。同樣的在akka中也提供了自己的熔斷措施,即akka.pattern.CircuitBreaker

二、斷路器的幾種狀態

在正常操作期間,斷路器處於閉合狀態:

(1)超出配置的callTimeout的異常或呼叫會增加失敗計數器

(2)成功將故障計數重置為零

(3)當故障計數器達到maxFailures計數時,斷路器跳閘到開啟狀態

處於開啟狀態時:

(1)所有呼叫都是快速失敗的 CircuitBreakerOpenException

(2)在配置的resetTimeout之後,斷路器進入半開狀態

在半開狀態時:

(1)允許第一次嘗試通過而不會快速失敗

(2)所有其他呼叫失敗快速,異常與開啟狀態一樣

(3)如果第一次呼叫成功,則斷路器將重置為Closed狀態並重置resetTimeout

(4)如果第一次呼叫失敗,則斷路器再次跳閘到開路狀態(對於指數退避斷路器,resetTimeout乘以指數退避因子)

其各狀態之間之間的轉換如下圖所示:

三、例子

class DangerousActor extends Actor with ActorLogging {
import context.dispatcher

val breaker =
new CircuitBreaker(
context.system.scheduler,
//最大失敗次數設定為5
maxFailures = 5,
//超時時間設定為5s
callTimeout = 10.seconds,
//重置超時為1分鐘
resetTimeout = 1.minute).onOpen(notifyMeOnOpen())

def notifyMeOnOpen(): Unit =
log.warning("My CircuitBreaker is now open, and will not close for one minute")
//#circuit-breaker-initialization

//#circuit-breaker-usage
def dangerousCall: String = "This really isn't that dangerous of a call after all"

def receive = {
case "is my middle name" ⇒
breaker.withCircuitBreaker(Future(dangerousCall)) pipeTo sender()
case "block for me" ⇒
sender() ! breaker.withSyncCircuitBreaker(dangerousCall)
}
//#circuit-breaker-usage

}

在該示例中,我們使用了withCircuitBreaker一個非同步方法(該方法返回一個Future),例如從資料庫中檢索資料的呼叫,然後將結果傳回給傳送方。如果由於某種原因,此示例中的資料庫沒有響應,或者存在另一個問題,則斷路器將開啟並停止嘗試一次又一次地擊中資料庫,直到超時結束。