2016-12-14 6 views
2

Я пробовал свою руку на scala + Akka, и я пытаюсь выяснить устойчивость к ошибкам. У меня есть актер, который получает сообщение от супервизора и вставляет данные в БД. Супервизор перезапускает актера, когда он испытывает ошибку.Akka Подход к отказоустойчивости

Я изменяю строку соединения в postRestart() в случае возникновения проблем с подключением к БД. Теперь, когда есть проблема с подключением с одной БД, актер перезагружается и начинает вставлять данные в другую БД.

Это хороший подход? Каков рекомендуемый подход?

Руководитель:

class SocialSupervisor extends Actor { 

    override val supervisorStrategy=OneForOneStrategy(loggingEnabled = false){ 
    case (e:Exception)=>Restart 
    } 

    val post_ref=context.actorOf(Props[Post]) 
    def receive={ 
      case Get_Feed(feed)=>{ 
       //get data from feed 
       post_ref!Post_Message(posted_by,post) 
      } 
    } 
} 

Актер:

class Post extends Actor{ 
    val config1=ConfigFactory.load() 
    var config=config1.getConfig("MyApp.db") 

    override def postRestart(reason: Throwable) { 
     config=config1.getConfig("MyApp.backup_db") 
     super.postRestart(reason) 
    } 

    def insert_data(commented_by:String,comment:String){ 
      val connection_string=config.getString("url") 
       val username=config.getString("username") 
       val password=config.getString("password") 
       //DB operations 
    } 

    def receive={ 
     case Post_Message(posted_by,message)=>{ 
     insert_data(posted_by, message) 
     } 
    } 
} 

ответ

1

Я думаю, что есть некоторые улучшения вы могли бы сделать, чтобы ваш код, чтобы сделать его более "отказоустойчивая".

Модульность

Вы, вероятно, следует отделить insert_data функцию от остальной части актера, так что он может быть использован & испытания независимо от любого ActorSystem. Ваши актеры должны иметь очень мало коду в них и метод receive должен быть в основном диспетчером внешних функций:

object Post { 
    def insert_data(conn : Connection)(commented_by : String, comment : String) = { 
    ... 
    } 
} 

Вы можете даже пойти на один шаг дальше и удалить Connection зависимости. С точки зрения вашего актера вставка не более чем функция, которая принимает в PostMessage и возвращает число действительных обновлений строк:

object Post { 
    //returns an Int because Statement.executeUpdate returns an Int 
    type DBInserter : Post_Message => Int 

Теперь вы можете вставить в базу данных подключения, как и раньше:

def insertIntoLiveDB(connFactory :() => Connection) : DBInserter = 
    (postMessage : Post_Message) => { 
     val sqlStr = s"INSERT INTO .." 
     connFactory().createStatement() executeUpdate sqlStr 
    } 
    } 

Или написать функцию, которая никогда не делает вставки для целей тестирования: не

//does no inserting 
    val neverInsert : DBInserter = (postMessage : Post_Message) => 0 
} 

Теперь ваш актер имеет практически никакой логики:

class Post(inserter : Post.DBInserter) extends Actor { 

    def receive = { 
    case pm : Post_Message => inserter(pm) 
    } 

} 

Отказоустойчивость

До сих пор основной источник «вина» в приложении является сеть, которая проявляется в вашем случае с помощью Connection к базе данных. Нам нужно, чтобы Connections автоматически обновлялись в случае сбоя. Мы можем использовать фабричную функцию сделать так:

def basicConnFactory(timeoutInSecs : Int = 10) = { 

    //setup initial connection, not specified in question 
    var conn : Connection = ??? 

() => { 
    if(conn isValid timeoutInSecs) 
     conn 
    else { 
     conn = ??? //setup backup connection 
     conn 
    } 
    } 
} 

Теперь срок действия данного соединения испытывается на каждую вставку и восстановил, если есть проблема. Завод может быть использован для создания актера:

import Post.queryLiveDB 
val post_ref = 
    context actorOf (Props[Post], insertIntoLiveDB(basicConnFactory())) 

Как ваши производственные требования получить более строгими, вы можете ammend фабрики использовать connection pool ...

Смежные вопросы