无响应的actor系统:ThreadPoolExecutor调度程序只创建核心线程池,显然忽略了最大线程池大小

Zim*_*oot 5 java multithreading scala blocking akka

更新:如果我将ThreadPoolExecutor's核心池大小设置为与最大池大小(29个线程)相同,我发现我的程序仍然响应.但是,如果我将核心池大小设置为11并将最大池大小设置为29,那么actor系统只会创建11个线程.如何配置ActorSystem/ ThreadPoolExecutor继续创建线程以超过核心线程数并保持在最大线程数内?我宁愿不将核心线程数设置为最大线程数,因为我只需要额外的线程来取消作业(这应该是一个罕见的事件).


我有一个针对Oracle数据库运行的批处理程序,使用Java/Akka类型的actor与以下actor进行实现:

  1. BatchManager负责与REST控制器通信.它管理一些Queue未初始化的批处理作业; 当从队列中轮询未初始化的批处理作业时,它将变为一个JobManageractor并执行.
  2. JobManager维护一个存储过程队列和一个池Workers; 它Worker使用存储过程初始化每个,并在Worker完成后将过程的结果发送给JobManager,JobManager并将另一个存储过程发送给Worker.当作业队列为空且全部Workers空闲时,批处理终止,此时将JobManager结果报告给BatchManager,关闭其工作人员(通过TypedActor.context().stop()),然后自行关闭.该JobManager有一个Promise<Status> completion在作业成功完成,否则当作业由于取消或致命异常终止调查结束.
  3. Worker执行存储过程.它创建OracleConnection和用于执行存储过程的CallableStatementonFailure,JobManager.completion并向abort连接和cancel语句注册回调.此回调不使用actor系统的执行上下文,而是使用从创建的高速缓存执行程序服务创建的执行上下文BatchManager.

我的配置是

{"akka" : { "actor" : { "default-dispatcher" : {
    "type" : "Dispatcher",
    "executor" : "default-executor",
    "throughput" : "1",
    "default-executor" : { "fallback" : "thread-pool-executor" }
    "thread-pool-executor" : {
        "keep-alive-time" : "60s",
        "core-pool-size-min" : coreActorCount,
        "core-pool-size-max" : coreActorCount,
        "max-pool-size-min" : maxActorCount,
        "max-pool-size-max" : maxActorCount,
        "task-queue-size" : "-1",
        "task-queue-type" : "linked",
        "allow-core-timeout" : "on"
}}}}}
Run Code Online (Sandbox Code Playgroud)

目前workerCount = 8,其他地方配置了工人数量; coreActorCountworkerCount + 3同时maxActorCountworkerCount * 3 + 5.我在具有两个内核和8GB内存的Macbook Pro 10上进行测试; 生产服务器要大得多.我正在谈论的数据库背后是一个非常慢的VPN.我使用Oracle的JavaSE 1.8 JVM运行所有这些.本地服务器是Tomcat 7. Oracle JDBC驱动程序是10.2版(我可能会说服使用更新版本的权力).所有方法都返回void或者Future<>应该是非阻塞的.

当一个批次成功终止时,则没有问题 - 下一个批次立即启动,并提供完整的工作人员.但是,如果我通过终止当前批处理JobManager#completion.tryFailure(new CancellationException("Batch cancelled")),则onFailure通过已注册的回调Workers关火,然后系统变得反应迟钝.调试printlns表明新批处理从八个正常运行的工作程序中的三个开始,并且BatchManager变得完全没有响应(我添加了一个Future<String> ping只返回a 的命令,Futures.successful("ping")这也超时了).该onFailure回调执行在一个单独的线程池,即使他们是在演员系统的线程池,我应该有足够高max-pool-size,以适应原来JobManager,它的Workers,它的onFailure回调,第二JobManager,是Workers.相反,我似乎正在容纳原始JobManager和它的Workers,新的JobManager,不到一半的Workers,并没有留下任何剩余BatchManager. 的计算机我正在运行这是资源短缺,但它似乎应该能够运行十几个主题.

这是配置问题吗?这是由于JVM强加的限制和/或Tomcat强加的限制吗?这是由于我如何处理阻止IO的问题?可能还有其他一些我可能做错的事情,这些只是我想到的.

CancellableStatement的要点,其中CallableStatementOracleConnection取消

CancellableStatements创造不可变的地方的要点

JobManager清理代码的要点

通过获得的配置转储System.out.println(mergedConfig.toString());


编辑:我相信我已经将问题缩小到了演员系统(无论是配置还是与阻止数据库调用的交互).我删除了Worker演员,并将他们的工作量转移到Runnables固定大小的执行ThreadPoolExecutor,每个人JobManager创建自己的工作负载ThreadPoolExecutor并在批处理完成时关闭它(shutDown在正常终止时,shutDownNow在异常终止时).取消在实例化的缓存线程池上运行BatchManager.actor系统的调度程序仍然是一个ThreadPoolExecutor但只有六个线程分配给它.使用此备用设置,取消按预期执行 - 工作程序在其数据库连接中止时终止,并且new将JobManager立即使用完整的工作线程执行.这向我表明这不是硬件/ JVM/Tomcat问题.


更新:我使用Eclipse的Memory Analyzer进行了线程转储.我发现取消线程都挂了CallableStatement.close(),所以我重新排序取消,以便OracleConnection.abort()CallableStatement.cancel()此之前解决问题 - 取消所有(显然)正确执行.Worker尽管如此,线程继续依赖于他们的陈述 - 我怀疑我的VPN可能部分或完全归咎于此.

PerformanceAsync-akka.actor.default-dispatcher-19
  at java.net.SocketInputStream.socketRead0(Ljava/io/FileDescriptor;[BIII)I (Native Method)
  at java.net.SocketInputStream.read([BIII)I (SocketInputStream.java:150)
  at java.net.SocketInputStream.read([BII)I (SocketInputStream.java:121)
  at oracle.net.ns.Packet.receive()V (Unknown Source)
  at oracle.net.ns.DataPacket.receive()V (Unknown Source)
  at oracle.net.ns.NetInputStream.getNextPacket()V (Unknown Source)
  at oracle.net.ns.NetInputStream.read([BII)I (Unknown Source)
  at oracle.net.ns.NetInputStream.read([B)I (Unknown Source)
  at oracle.net.ns.NetInputStream.read()I (Unknown Source)
  at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1()S (T4CMAREngine.java:1109)
  at oracle.jdbc.driver.T4CMAREngine.unmarshalSB1()B (T4CMAREngine.java:1080)
  at oracle.jdbc.driver.T4C8Oall.receive()V (T4C8Oall.java:485)
  at oracle.jdbc.driver.T4CCallableStatement.doOall8(ZZZZ)V (T4CCallableStatement.java:218)
  at oracle.jdbc.driver.T4CCallableStatement.executeForRows(Z)V (T4CCallableStatement.java:971)
  at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout()V (OracleStatement.java:1192)
  at oracle.jdbc.driver.OraclePreparedStatement.executeInternal()I (OraclePreparedStatement.java:3415)
  at oracle.jdbc.driver.OraclePreparedStatement.execute()Z (OraclePreparedStatement.java:3521)
  at oracle.jdbc.driver.OracleCallableStatement.execute()Z (OracleCallableStatement.java:4612)
  at com.util.CPProcExecutor.execute(Loracle/jdbc/OracleConnection;Ljava/sql/CallableStatement;Lcom/controller/BaseJobRequest;)V (CPProcExecutor.java:57)
Run Code Online (Sandbox Code Playgroud)

然而,即使在修复取消订单后,我仍然遇到演员系统没有创建足够线程的问题:我仍然只在新批次中获得八个工人中的三个,新工人被添加为已取消的工作人员他们的网络连接超时.我总共有11个线程 - 我的核心池大小,29个线程 - 我的最大池大小.显然,actor系统忽略了我的max pool size参数,或者我没有正确配置max pool size.

小智 5

(免责声明:我不知道Akka)

通过你的queue-size = -1的下面配置,我想,任务队列是无界的.

  "task-queue-size": "-1",
  "task-queue-type": "linked"
Run Code Online (Sandbox Code Playgroud)

除非工作队列已满并且无法排队,否则ThreadPoolExecutor不会产生核心池大小.仅当任务队列已满时,它才会开始产生最多线程.

如果运行的corePoolSize线程少于corePoolSize,则Executor总是更喜欢添加新线程而不是排队.如果corePoolSize或更多线程正在运行,则Executor总是更喜欢排队请求而不是添加新线程.如果请求无法排队,则会创建一个新线程,除非它超过maximumPoolSize,在这种情况下,该任务将被拒绝.

请检查您是否可以修复有限的队列大小,并查看线程是否增加到最大线程数.谢谢.