更新:如果我将ThreadPoolExecutor's
核心池大小设置为与最大池大小(29个线程)相同,我发现我的程序仍然保持响应。但是,如果我将核心池大小设置为11并将最大池大小设置为29,那么actor系统只会创建11个线程。如何配置ActorSystem
/ ThreadPoolExecutor
以继续创建线程以超过核心线程数并保持在最大线程数内?我宁愿不将核心线程数设置为最大线程数,因为我只需要额外的线程来取消作业(这应该是一个罕见的事件)。
我有一个针对Oracle数据库运行的批处理程序,使用Java / Akka类型的actor与以下actor一起实现:
BatchManager
负责与REST控制器通信。它管理Queue
未初始化的批处理作业;当从队列中轮询未初始化的批处理作业时,它将变为JobManager
actor并执行。JobManager
维护一个存储过程队列和一个Workers
池;它使用存储过程初始化每个Worker
,当Worker
完成时,它将过程的结果发送到JobManager
,JobManager
发送另一个存储过程到Worker
。当作业队列为空且所有Workers
都空闲时,批处理终止,此时JobManager
将其结果报告给BatchManager
,关闭其工作人员(通过TypedActor.context().stop()
),然后关闭自己。 JobManager
有Promise<Status> completion
在作业成功完成时完成,或者当作业因取消或致命异常而终止时完成。Worker
执行存储过程。它创建用于执行存储过程的OracleConnection和CallableStatement,并向onFailure
注册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"
}}}}}
工作人员的数量在其他地方配置,目前为workerCount = 8
; coreActorCount
为workerCount + 3
,而maxActorCount
为workerCount * 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
命令只返回Futures.successful("ping")
,这也超时了)。 onFailure
回调正在一个单独的线程池上执行,即使它们在actor系统的线程池中,我应该有足够高的max-pool-size
来容纳原始JobManager
,Workers
,onFailure
回调,第二个JobManager
,Workers
。相反,我似乎正在容纳原始的JobManager
及其Workers
,新的JobManager
以及不到Workers
的一半,并且BatchManager.
没有遗留任何内容。我运行它的计算机缺乏资源,但它似乎应该可以运行十几个线程。
这是配置问题吗?这是由于JVM强加的限制和/或Tomcat强加的限制吗?这是由于我如何处理阻止IO的问题?可能还有其他一些我可能做错的事情,这些只是我想到的。
Gist of CancellableStatement CallableStatement
和OracleConnection
被取消
Gist of Immutable其中CancellableStatements
已创建
Gist of JobManager's cleanup code
Config dump通过System.out.println(mergedConfig.toString());
编辑:我相信我已经将问题缩小到了演员系统(无论是配置还是与阻止数据库调用的交互)。我删除了Worker
个参与者,并将他们的工作量移到Runnables
,该ThreadPoolExecutor
执行固定大小JobManager
,其中每个ThreadPoolExecutor
创建自己的shutDown
并关闭它批处理完成时关闭(正常终止时shutDownNow
,特殊终止时BatchManager
。取消在ThreadPoolExecutor
中实例化的缓存线程池上运行。演员系统的调度程序仍然是JobManager
,但只分配了六个线程。使用此备用设置,取消按预期执行 - 工作程序在数据库连接中止时终止,新CallableStatement.close()
立即执行完整的工作线程。这向我表明这不是硬件/ JVM / Tomcat问题。
更新:我使用Eclipse's Memory Analyzer进行了线程转储。我发现取消线程挂在OracleConnection.abort()
上,因此我重新排序取消,以便CallableStatement.cancel()
在Worker
之前,这解决了问题 - 取消所有(显然)正确执行。尽管如此,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)
线程继续依赖于他们的陈述 - 我怀疑我的VPN可能部分或完全归咎于此。
{{1}}
然而,即使在确定取消订单后,我仍然遇到演员系统没有创建足够线程的问题:我仍然只有新工人中的八个工人中的三个,新工人被取消的工作人员的网络连接超时。总共我有11个线程 - 我的核心池大小,29个线程 - 我的最大池大小。显然,actor系统忽略了我的max pool size参数,或者我没有正确配置max pool size。
答案 0 :(得分:5)
(免责声明:我不认识Akka)
通过你的queue-size = -1的下面配置,我猜,任务队列是无界限的。
"task-queue-size": "-1",
"task-queue-type": "linked"
除非工作队列已满并且无法排队,否则ThreadPoolExecutor不会产生核心池大小。只有当任务队列已满时,它才会开始产生最多的线程。
如果正在运行少于corePoolSize的线程,则始终执行Executor 喜欢添加新线程而不是排队。如果是corePoolSize或 正在运行更多线程,Executor总是更喜欢排队 请求而不是添加新线程。如果请求不能 排队,创建一个新线程,除非这会超过 maximumPoolSize,在这种情况下,任务将被拒绝。
请检查您是否可以修复有限的队列大小,并查看线程是否增加到最大线程数。感谢。
答案 1 :(得分:0)
没有足够的代码来提供解决方案,但是当系统无响应时,您可以检查系统资源利用率(cpu,ram)是否未更改,请检查Oracle数据库。
如果你杀死一组连接,那么另一个工作立即启动:我想在oracle级别有一些阻塞会话(一个未提交的写入事务阻止了对同一资源的另一个写入事务)。
当处于无响应状态时,请检查阻止会话:
SELECT s1.username || '@' || s1.machine
|| ' ( SID=' || s1.sid || ' ) is blocking '
|| s2.username || '@' || s2.machine || ' ( SID=' || s2.sid || ' ) ' AS blocking_status
FROM v$lock l1, v$session s1, v$lock l2, v$session s2
WHERE s1.sid=l1.sid AND s2.sid=l2.sid
AND l1.BLOCK=1 AND l2.request > 0
AND l1.id1 = l2.id1
AND l1.id2 = l2.id2