Akka容错方法

时间:2016-12-14 10:37:14

标签: scala akka

我正在尝试scala + Akka,我正在努力找出容错能力。我有一个演员从主管接收消息并将数据插入数据库。当主管遇到故障时,Supervisor会重新启动它。

我正在更改postRestart()中的连接字符串,以防数据库出现连接问题。现在,只要一个数据库出现连接问题,actor就会重新启动并开始将数据插入另一个数据库。

这是一个足够好的方法吗?推荐的方法是什么?

监:

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 个答案:

答案 0 :(得分:1)

我认为您可以对代码进行一些改进,使其更具有容错能力,并且#34;

<强>模块化

您应该将insert_data功能与Actor的其余部分分开,以便可以使用它。测试独立于任何ActorSystem。您的Actors中的代码应该很少,而receive方法基本上应该是外部函数的调度程序:

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

您甚至可以更进一步,删除Connection依赖项。从你的Actor的角度来看,插入只不过是一个函数,它接收PostMessage并返回有效行更新的数量:

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

您现在可以像以前一样插入数据库Connection:

  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)
  }

}

容错

到目前为止&#34;错误的最大来源&#34;在应用程序中是网络,在您的情况下由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
     }
  }
}

现在,在每次插入时测试Connection的有效性,如果出现问题则重新建立。然后可以使用此工厂创建Actor:

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

随着您的生产要求变得更加严格,您可以修改工厂以使用connection pool ......