我正在尝试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)
}
}
}
答案 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 ......