了解 play/scala 错误处理
Understanding play/scala error handling
我在 Scala/Play 中有一个 Web 应用程序,使用 Cassandra 作为数据库。我在测试潜在错误时 运行 遇到以下问题。
下面是我项目的组成部分
Application.scala -> 基本控制器
Model.scala -> 持有
业务逻辑
CassandraClient.scala -> 逻辑连接
cassandra 和 运行 查询 cassandra
CassandraClient.scala 如下所示
object CassandraClient {
private val cluster = Cluster.builder()
.withLoadBalancingPolicy(
new WhiteListPolicy(new RoundRobinPolicy(), nodes))
.withSocketOptions(socketOptions)
.addContactPointsWithPorts(nodes)
.withCredentials(CASSANDRA_USERNAME, CASSANDRA_PWD).build()
}
private val session = cluster.connect()
def getValueFromCassandraTable(token:String) = {
var query = QueryBuilder.select.all()...
seesion.execute(query)
}
Cassandra连接是在我第一次调用getValueFromCassandraTable时建立的。由于 CassandraClient 是一个对象,连接到 Cassandra 的逻辑只被调用一次。
Model.Scala 有一些代码来处理 session.execute 返回的 future。
例如:查看下面 Model.scala 中的示例代码。
def getTitles(titles: String)(implicit ctxt: ExecutionContext): Future[List[<Sometype>]] = {
try{
CassandraClient.getValueFromCassandraTable(token).toScalaFuture.map { rows =>
rows.map(row => row("value").toList
}.recover { case e: Exception =>List()}
}
} catch{case e:Exception =>
Logger.info("THIS DOES NOT EXECUTE??")
Future{List()}}
}
现在,当上面的示例代码第一次执行时,session.execute() 将被执行。然后执行 getValueFromCassandraTable。
所以我认为执行流程是
Models.scala -> session.execute() -> getValueFromCassandraTable()
中的代码片段
因此,如果 session.execute 失败,我应该能够在 try catch 异常块中捕获它。但令我惊讶的是,当 session.execute 失败时,catch 块没有被执行。而是播放框架抛出异常。谁能解释一下这种行为。
异常堆栈
Caused by: java.lang.RuntimeException: java.lang.ExceptionInInitializerError
at play.api.mvc.ActionBuilder$$anon.apply(Action.scala:498) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply$$anonfun$apply.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply$$anonfun$apply.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply.apply(Action.scala:104) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
at scala.Option.map(Option.scala:145) ~[scala-library-2.10.5.jar:na]
at play.api.mvc.Action$$anonfun$apply.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply.apply(Action.scala:96) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.DoneIteratee$$anonfun$mapM.apply(Iteratee.scala:741) ~[play-iteratees_2.10-2.4.2.jar:2.4.2]
Caused by: java.lang.ExceptionInInitializerError: null
at models.Model$.getTitles(Model.scala:121) ~[classes/:na]
at models.Model$.getMatchingTitles(Model.scala:48) ~[classes/:na]
at models.Model$.getMatchingTitles(Model.scala:56) ~[classes/:na]
at controllers.Application$$anonfun$searchTitle.apply(Application.scala:15) ~[classes/:na]
at controllers.Application$$anonfun$searchTitle.apply(Application.scala:15) ~[classes/:na]
at play.api.mvc.ActionBuilder$$anonfun$async.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.ActionBuilder$$anonfun$async.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$.invokeBlock(Action.scala:533) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$.invokeBlock(Action.scala:530) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.ActionBuilder$$anon.apply(Action.scala:493) ~[play_2.10-2.4.2.jar:2.4.2]
Caused by: com.datastax.driver.core.exceptions.AuthenticationException: Authentication error on host /10.65.5.44:9042: Username and/or password are incorrect
at com.datastax.driver.core.Connection.apply(Connection.java:368) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection.apply(Connection.java:338) ~[cassandra-driver-core-2.1.6.jar:na]
at com.google.common.util.concurrent.Futures$ChainingListenableFuture.run(Futures.java:861) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:297) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.ExecutionList.executeListener(ExecutionList.java:156) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.ExecutionList.execute(ExecutionList.java:145) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.AbstractFuture.set(AbstractFuture.java:185) ~[guava-16.0.1.jar:na]
at com.datastax.driver.core.Connection$Future.onSet(Connection.java:1170) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:1000) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:922) ~[cassandra-driver-core-2.1.6.jar:na]
Modle.scala中的Lin#121是调用CassandrClient.getValueFromCassandraTable的那个。
我通过传递连接到 cassandra 的错误密码来模拟异常,因此获得 AuthroizationException。我希望在 Try catch 中捕获此异常。但它没有被抓住。
(请注意,这与 Future 无关,因为 session.executAysnc 甚至在异常发生时都没有被调用。所以 futures 还没有发挥作用。)
--编辑--
看起来 Play 正在吞下 Exception 并抛出 Error 对象。不确定为什么会这样。
例如,如果您不使用 Await,则代码不会打印 "InterruptedException test"。
try {
val exception = Future {
throw new InterruptedException("exception")
}.recover { case e: Exception => "ok" }
exception.onComplete {
case Success(a) => println(a)
case Failure(err) => println(err)
}
//Await.result(exception, 1 seconds)
} catch {
case e: Exception => println("InterruptedException test")
}
session.execute() 可能会失败并出现类似 IOError 的错误。而你只捕获异常。
检查你是否没有错误,只是微不足道的异常。如果您将堆栈跟踪添加到您的问题,那就太好了。
你可以尝试捕获任何Throwable(实际项目中不要这样做,你可以只为了调试而尝试)
try {
.... your code ...
} catch {
case _ => errorHandler(e)
}
只有在 Future 中才会捕获错误。
编辑:
我的 cassandra 数据库上的实例。
如果在连接到 cassandra 期间出现异常,它会显示用户列表或 "something going wrong" 字符串。例如错误的节点或 cassandra 正在关闭。
package controllers
import com.datastax.driver.core.Cluster
import com.datastax.driver.core.Session
import scala.collection.JavaConversions._
import play.api._
import play.api.mvc._
class Application extends Controller {
def index = Action {
try{
var b = new StringBuilder
for (row <- CassandraClient.getValueFromCassandraTable()) {
b ++= row.getString("user_id")
b ++= "\n"
}
Ok(b.toString())
} catch {
case _ => InternalServerError("something going wrong")
}
}
}
object CassandraClient {
private val cluster = Cluster.builder()
.addContactPoint("localhost")
.withPort(9042)
.build()
val session = cluster.connect()
def getValueFromCassandraTable() = {
session.execute("SELECT * FROM mykeyspace.users")
}
}
我在 Scala/Play 中有一个 Web 应用程序,使用 Cassandra 作为数据库。我在测试潜在错误时 运行 遇到以下问题。
下面是我项目的组成部分
Application.scala -> 基本控制器
Model.scala -> 持有 业务逻辑
CassandraClient.scala -> 逻辑连接 cassandra 和 运行 查询 cassandra
CassandraClient.scala 如下所示
object CassandraClient {
private val cluster = Cluster.builder()
.withLoadBalancingPolicy(
new WhiteListPolicy(new RoundRobinPolicy(), nodes))
.withSocketOptions(socketOptions)
.addContactPointsWithPorts(nodes)
.withCredentials(CASSANDRA_USERNAME, CASSANDRA_PWD).build()
}
private val session = cluster.connect()
def getValueFromCassandraTable(token:String) = {
var query = QueryBuilder.select.all()...
seesion.execute(query)
}
Cassandra连接是在我第一次调用getValueFromCassandraTable时建立的。由于 CassandraClient 是一个对象,连接到 Cassandra 的逻辑只被调用一次。
Model.Scala 有一些代码来处理 session.execute 返回的 future。
例如:查看下面 Model.scala 中的示例代码。
def getTitles(titles: String)(implicit ctxt: ExecutionContext): Future[List[<Sometype>]] = {
try{
CassandraClient.getValueFromCassandraTable(token).toScalaFuture.map { rows =>
rows.map(row => row("value").toList
}.recover { case e: Exception =>List()}
}
} catch{case e:Exception =>
Logger.info("THIS DOES NOT EXECUTE??")
Future{List()}}
}
现在,当上面的示例代码第一次执行时,session.execute() 将被执行。然后执行 getValueFromCassandraTable。 所以我认为执行流程是 Models.scala -> session.execute() -> getValueFromCassandraTable()
中的代码片段因此,如果 session.execute 失败,我应该能够在 try catch 异常块中捕获它。但令我惊讶的是,当 session.execute 失败时,catch 块没有被执行。而是播放框架抛出异常。谁能解释一下这种行为。
异常堆栈
Caused by: java.lang.RuntimeException: java.lang.ExceptionInInitializerError
at play.api.mvc.ActionBuilder$$anon.apply(Action.scala:498) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply$$anonfun$apply.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply$$anonfun$apply.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply.apply(Action.scala:104) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$$anonfun$apply.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
at scala.Option.map(Option.scala:145) ~[scala-library-2.10.5.jar:na]
at play.api.mvc.Action$$anonfun$apply.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply.apply(Action.scala:96) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.DoneIteratee$$anonfun$mapM.apply(Iteratee.scala:741) ~[play-iteratees_2.10-2.4.2.jar:2.4.2]
Caused by: java.lang.ExceptionInInitializerError: null
at models.Model$.getTitles(Model.scala:121) ~[classes/:na]
at models.Model$.getMatchingTitles(Model.scala:48) ~[classes/:na]
at models.Model$.getMatchingTitles(Model.scala:56) ~[classes/:na]
at controllers.Application$$anonfun$searchTitle.apply(Application.scala:15) ~[classes/:na]
at controllers.Application$$anonfun$searchTitle.apply(Application.scala:15) ~[classes/:na]
at play.api.mvc.ActionBuilder$$anonfun$async.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.ActionBuilder$$anonfun$async.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$.invokeBlock(Action.scala:533) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$.invokeBlock(Action.scala:530) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.ActionBuilder$$anon.apply(Action.scala:493) ~[play_2.10-2.4.2.jar:2.4.2]
Caused by: com.datastax.driver.core.exceptions.AuthenticationException: Authentication error on host /10.65.5.44:9042: Username and/or password are incorrect
at com.datastax.driver.core.Connection.apply(Connection.java:368) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection.apply(Connection.java:338) ~[cassandra-driver-core-2.1.6.jar:na]
at com.google.common.util.concurrent.Futures$ChainingListenableFuture.run(Futures.java:861) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:297) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.ExecutionList.executeListener(ExecutionList.java:156) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.ExecutionList.execute(ExecutionList.java:145) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.AbstractFuture.set(AbstractFuture.java:185) ~[guava-16.0.1.jar:na]
at com.datastax.driver.core.Connection$Future.onSet(Connection.java:1170) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:1000) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:922) ~[cassandra-driver-core-2.1.6.jar:na]
Modle.scala中的Lin#121是调用CassandrClient.getValueFromCassandraTable的那个。 我通过传递连接到 cassandra 的错误密码来模拟异常,因此获得 AuthroizationException。我希望在 Try catch 中捕获此异常。但它没有被抓住。
(请注意,这与 Future 无关,因为 session.executAysnc 甚至在异常发生时都没有被调用。所以 futures 还没有发挥作用。)
--编辑--
看起来 Play 正在吞下 Exception 并抛出 Error 对象。不确定为什么会这样。
例如,如果您不使用 Await,则代码不会打印 "InterruptedException test"。
try {
val exception = Future {
throw new InterruptedException("exception")
}.recover { case e: Exception => "ok" }
exception.onComplete {
case Success(a) => println(a)
case Failure(err) => println(err)
}
//Await.result(exception, 1 seconds)
} catch {
case e: Exception => println("InterruptedException test")
}
session.execute() 可能会失败并出现类似 IOError 的错误。而你只捕获异常。
检查你是否没有错误,只是微不足道的异常。如果您将堆栈跟踪添加到您的问题,那就太好了。
你可以尝试捕获任何Throwable(实际项目中不要这样做,你可以只为了调试而尝试)
try {
.... your code ...
} catch {
case _ => errorHandler(e)
}
只有在 Future 中才会捕获错误。
编辑:
我的 cassandra 数据库上的实例。 如果在连接到 cassandra 期间出现异常,它会显示用户列表或 "something going wrong" 字符串。例如错误的节点或 cassandra 正在关闭。
package controllers
import com.datastax.driver.core.Cluster
import com.datastax.driver.core.Session
import scala.collection.JavaConversions._
import play.api._
import play.api.mvc._
class Application extends Controller {
def index = Action {
try{
var b = new StringBuilder
for (row <- CassandraClient.getValueFromCassandraTable()) {
b ++= row.getString("user_id")
b ++= "\n"
}
Ok(b.toString())
} catch {
case _ => InternalServerError("something going wrong")
}
}
}
object CassandraClient {
private val cluster = Cluster.builder()
.addContactPoint("localhost")
.withPort(9042)
.build()
val session = cluster.connect()
def getValueFromCassandraTable() = {
session.execute("SELECT * FROM mykeyspace.users")
}
}