2014-12-24 96 views
1

我正在使用scala玩遊戲框架。我還使用RedisScala驅動程序(這一個https://github.com/etaty/rediscala)與Redis進行通信。如果Redis不包含數據,那麼我的應用程序正在MongoDB中查找數據。 當Redis發生故障或僅僅因爲某種原因不可用時,應用程序等待響應時間過長。如何在這種情況下實現故障轉移策略。如果請求花費太長時間,我想停止請求Redis。在Redis重新聯機時開始與他們合作。 爲了澄清這個問題我的代碼是像現在以下RedisClient失敗策略

private def getUserInfo(userName: String): Future[Option[UserInfo]] = { 
    CacheRepository.getBaseUserInfo(userName) flatMap{ 
     case Some(userInfo) => 
     Logger.trace(s"AuthenticatedAction.getUserInfo($userName). User has been found in cache") 
     Future.successful(Some(userInfo)) 
     case None => 
     getUserFromMongo(userName) 
    } 
    } 

回答

2

我認爲你需要有下列情形之間的區別(在它們發生的可能性順序):

  1. 沒有在高速緩存中的數據(Redis) - 我猜在這種情況下,Redis將很快返回,您必須從Mongo中獲得它。在上面的代碼中,您需要在從Mongo獲取它之後在Redis中設置數據,以便在後續調用中將其放入緩存中。

    您需要將您的RedisClient包裝在應用程序代碼中,以瞭解任何斷開/重新連接。基本上有兩個狀態 - 第一,當Redis正常工作時,第二,當Redis下降/慢時。

  2. Redis很慢 - 由於以下原因之一,可能會發生這種情況。
    2.1。 網絡很慢:同樣,你不能做太多的事情,但要將消息返回給客戶端。如果你的網絡本身很慢,去Mongo不太可能解決這個問題。

    2.2。 操作很慢:例如,如果您嘗試獲取大量數據,或者您正在有序集上運行範圍查詢,則會發生這種情況。在這種情況下,您需要重新訪問使用您在Redis中存儲的數據量的Redis數據結構。但是,看起來像你的例子,這不會是一個問題。單一Redis獲得操作通常是LAN上的低延遲。

  3. Redis節點無法訪問 - 我不確定這種情況發生的頻率,除非您的網絡出現故障。在這種情況下,您也無法連接到MongoDB。我相信,當運行Redis的節點關閉或磁盤已滿時,也可能發生這種情況。因此,您應該在設計中處理此問題。話雖如此,Rediscala客戶端將自動檢測到任何斷開連接並自動重新連接。我個人已經這樣做了。停止並更新Redis版本並重新啓動Redis而不觸及正在運行的客戶端(JVM)。

最後,您可以在上面的程序中使用帶超時的Future(參見 - Scala Futures - built in timeout?)。如果未來超時未完成,您可以採取其他操作(轉到Mongo或向用戶返回錯誤消息)。鑑於#1和#2可能比#3更頻繁地發生,您的超時值應該反映這兩種情況。鑑於局域網上#1和#2的速度很快,您可以以100ms的超時值開始。

+0

感謝您提供如此詳細的概述。第1點正是我目前正在做的。我的問題主要是關於第3點。據我看到RedisScala客戶端有演員試圖每2秒重新連接,但我不知道如何從它獲得有關連接的信息。如果我可以從RedisScala獲得這些信息,那麼當Redis無法訪問時,我可能會錯過請求數據的步驟。我認爲最簡單的方法就是使用Future,並在最後提到超時。謝謝你的幫助。 – Kir

0

Soumya Simanta提供了詳細的答案,我只想發佈我用於超時的代碼。該代碼需要我的項目中使用的Play框架

private def get[B](key: String, valueExtractor: Map[String, ByteString] => Option[B], logErrorMessage: String): Future[Option[B]] = { 
    val timeoutFuture = Promise.timeout(None, Duration(Settings.redisTimeout)) 
    val mayBeHaveData = redisClient.hgetall(key) map { value => 
     valueExtractor(value) 
    } recover{ 
     case e => 
     Logger.info(logErrorMessage + e) 
     None 
    } 

    // if timeout occurred then None will be result of method 
    Future.firstCompletedOf(List(mayBeHaveData, timeoutFuture)) 
    }