【问题标题】:Understanding play/scala error handling了解 play/scala 错误处理
【发布时间】:2015-09-11 07:58:06
【问题描述】:

我在 Scala/Play 中有一个使用 Cassandra 作为数据库的 Web 应用程序。我在测试潜在错误时遇到了以下问题。

以下是我项目的组件

  1. Application.scala -> 基本控制器

  2. Model.scala -> 持有 业务逻辑

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

当我第一次调用 getValueFromCassandraTable 时,首先建立了 Cassandra 连接。由于 CassandraClient 是一个对象,连接到 Cassandra 的逻辑只被调用一次。

Model.Scala 有一些代码来处理 session.execute 返回的未来。

例如:查看下面 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 中的代码 sn-p -> session.execute() -> getValueFromCassandraTable()

所以,如果 session.execute 失败,我应该能够在 try catch 异常块中捕获它。但令我惊讶的是,当 session.execute 失败时,catch 块没有被执行。相反,play 框架会引发异常。有人可以解释这种行为吗?

异常堆栈

Caused by: java.lang.RuntimeException: java.lang.ExceptionInInitializerError
    at play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:498) ~[play_2.10-2.4.2.jar:2.4.2]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.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$1$$anonfun$apply$4.apply(Action.scala:104) ~[play_2.10-2.4.2.jar:2.4.2]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.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$1.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
    at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96) ~[play_2.10-2.4.2.jar:2.4.2]
    at play.api.libs.iteratee.DoneIteratee$$anonfun$mapM$2.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$1.apply(Application.scala:15) ~[classes/:na]
    at controllers.Application$$anonfun$searchTitle$1.apply(Application.scala:15) ~[classes/:na]
    at play.api.mvc.ActionBuilder$$anonfun$async$1.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
    at play.api.mvc.ActionBuilder$$anonfun$async$1.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$1.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$8.apply(Connection.java:368) ~[cassandra-driver-core-2.1.6.jar:na]
    at com.datastax.driver.core.Connection$8.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。因此,future 尚未发挥作用。)

--编辑--

看起来 Play 正在吞下 Exception 并抛出一个 Error 对象。不知道为什么会这样。

【问题讨论】:

  • 这个问题似乎纯粹是关于对Scala中Futures的理解。
  • 好吧,我不确定。 session.execute() 抛出异常,这不是异步调用。我不明白为什么我的 try catch 块没有捕获 session.execute 抛出的异常。
  • 使用recover {case _ =&gt; Future(List())}会被抓到吗?

标签: scala error-handling playframework-2.0 try-catch


【解决方案1】:

session.execute() 可能会因 IOError 等错误而失败。你只捕获异常。

检查你是否没有错误,只是微不足道的异常。如果您将堆栈跟踪添加到您的问题中,那就太好了。

您可以尝试捕获任何 Throwable(不要在实际项目中这样做,您可以尝试仅用于调试)

try {
  .... your code ...
} catch {
  case _ => errorHandler(e)
}

只有在未来的情况下才会捕获错误。

编辑:

我的 cassandra 数据库上的实时示例。 在连接到 cassandra 期间出现异常时,它会显示用户列表或“出现问题”字符串。例如错误的节点或 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")
      }
    }

【讨论】:

  • 是的,你是对的。 Play 抛出 ExceptionInInitializerError ,然后被 try catch 块捕获。但是,我对这种行为感到困惑。我的代码抛出 DriverException 类型的 AuthenticationException。 Play 似乎正在捕捉它抛出 ExceptionInInitializerError。关于发生了什么的任何解释?我发布了堆栈跟踪。
  • 此字符串“private val session = cluster.connect()”不在您的 try-catch 块中。调用“cluster.connect()”时会抛出 AuthenticationException。它不是由您的代码处理的,因此请在“ExceptionInInitializerError”中进行包装。我想你在播放开始时直接运行这段代码。
  • 嗯,cluster.connect() 是在对象 CassandraClient 的主体中定义的。所以我相信只有在第一次引用 CassandraClient 时才会调用它。因为这是一个游戏应用程序。只有当我对我的服务进行第一次 httpcall 时才会调用它。
  • 您确定在此之前没有在其他任何地方调用“CassandraClient”吗?也许它在某些“框架”中被调用?集群配置——socketOptions、节点——它们是如何定义的呢?
  • 是的,我没有在其他任何地方调用 CassandraClient。集群配置 - 套接字选项、节点等在 CassandraClient 对象中定义。我已经从上面的 sn-p 中省略了该代码。
【解决方案2】:

也许你有InterruptedException

例如,如果您不使用 Await,则代码不会打印“InterruptedException 测试”。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-24
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    • 2015-08-26
    • 1970-01-01
    • 2017-03-27
    • 2015-08-30
    相关资源
    最近更新 更多