使用play 2.2.1和JPA进行线程死锁

时间:2013-12-29 16:46:35

标签: playframework playframework-2.0 akka

我发现JPA有些奇怪的行为。在某些情况下,我们收到了错误

 "Timed out waiting for a free available connection."
 at com.jolbox.bonecp.DefaultConnectionStrategy.getConnectionInternal(DefaultConnectionStrategy.java:88) ~[bonecp.jar:na]

该行的来源位于:https://github.com/wwadge/bonecp/blob/master/bonecp/src/main/java/com/jolbox/bonecp/DefaultConnectionStrategy.java#L88

我做了简单的研究,发现了这个:

  1. Play2使用演员模型和akka。
  2. Play2将bonecp用于数据库连接池。
  3. 对于进程@Transaction请求,Play2至少使用2个不同的actor:

    从游泳池获取连接并处理呼叫的一个角色。

    相关代码:

    提交或回滚事务的第二个actor。此时发布连接。

    相关代码:

  4. 两个actor都是从一个akka执行器和线程池执行的。看:http://www.playframework.com/documentation/2.2.x/ThreadPools

    默认情况下有24个帖子。

  5. 默认情况下,30个连接可用于bonecp池。看:http://www.playframework.com/documentation/2.2.x/SettingsJDBC处的文档 和代码https://github.com/playframework/playframework/blob/master/framework/src/play-jdbc/src/main/scala/play/api/db/DB.scala#L372
  6. 假设我们有一个必须处理许多并发请求的高负载应用程序。在我的测试中,我遇到了50多个并发默认配置请求的问题。 您可以重复我的测试并仅使用非默认配置的3个并发请求来捕获此问题,如下所示:

    play {
      akka {
        akka.loggers = ["akka.event.Logging$DefaultLogger", "akka.event.slf4j.Slf4jLogger"]
       loglevel = DEBUG
        actor {
          default-dispatcher = {
            fork-join-executor {
             parallelism-factor = 1.0
              parallelism-min = 1
              parallelism-max = 1
            }
          }
        }
      }
    }
    
    ...
    db.default.minConnectionsPerPartition=2
    db.default.maxConnectionsPerPartition=2
    ...
    

    复杂性和处理时间对于此测试无关紧要。我的处理时间是7毫秒(解析参数+一个插入到数据库)。您只需要发送比连接池大小更多的并发请求。 现在我写下我认为正在发生的事情。

    1. 许多演员捕获了处理请求的线程和连接。
    2. 许多演员试图从游泳池获得连接。但目前没有可用的免费连接(所有其他演员捕获它们)。此连接池基于BlockingQueue。码: https://github.com/wwadge/bonecp/blob/master/bonecp/src/main/java/com/jolbox/bonecp/DefaultConnectionStrategy.java#L82

      因此当前线程在此行被阻止超时(默认为一秒)。

    3. 如果某些线程被此actor阻止,则某些actor无法使用此线程执行。
    4. 因此连接池返回的连接较少,因为可以用较少的线程执行较少的actor。
    5. 有时候所有线程都会被阻止。并且没有任何线程可用于执行actor,它们将返回到池的连接。
    6. 看起来像死锁。

      有人可以给我一个建议,我该如何避免这个问题?

2 个答案:

答案 0 :(得分:0)

  

有人可以给我一个建议,我该如何避免这个问题?

避免使用Akka actor直接向同一资源发出多个同时发生的潜在阻塞请求。使用单独的线程池来代替JDBC。

答案 1 :(得分:0)

实际上你的代码没有通过这里的演员,但它是在不同的线程之间传递。

您应该将线程池配置为较大,正如在本文档中阻止Java应用程序所做的那样:

http://www.playframework.com/documentation/2.2.x/ThreadPools