当我部署Play框架应用程序时,使用Akka框架到生产机器,它的行为与我的开发工作站不同。
这是一个接收一批设备IP地址的系统,它在每个设备上执行一些处理,并在处理完批次中的所有设备后聚合结果。这种处理不是CPU密集型的。
我基本上有两种类型的actor,一个BatchActor和一个DeviceActor。对于设备,我创建了一个由RoundRobinPool路由器和自定义调度程序支持的创建的actor。我试图一次处理~500个设备(并行)。
这个问题是,当我在我的OSX机器上运行此代码时,它会像我一样运行。
例如,如果我提交了一批200个设备的IP地址,则应用程序在我的工作站上并行运行所有设备。
但是,当我将此应用程序复制到生产计算机Red Hat Enterprise Linux(RHEL)并运行它提交相同的设备列表时,它一次只处理1到2个设备。
我需要做些什么来解决这个问题?
相关代码如下:
object Application extends Controller {
...
val numberOfWorkers = 500
val workers = Akka.system.actorOf(Props[DeviceActor]
.withRouter(RoundRobinPool(nrOfInstances = numberOfWorkers))
.withDispatcher("my-dispatcher")
)
def batchActor(config:BatchConfig)
= Akka.system.actorOf(BatchActor.props(workers, config), s"batch-${config.batchId}")
...
def batch = Action(parse.json) { request =>
request.body.validate[BatchConfig] match {
case config:BatchConfig => {
...
val batch = batchActor(config)
batch ! BatchActorProtocol.Start
Ok(Json.toJson(status))
}
...
}
}
application.conf配置部分如下所示:
my-dispatcher {
# Dispatcher is the name of the event-based dispatcher
type = Dispatcher
# What kind of ExecutionService to use
executor = "fork-join-executor"
# Configuration for the fork join pool
fork-join-executor {
# Min number of threads to cap factor-based parallelism number to
parallelism-min = 1000
# Parallelism (threads) ... ceil(available processors * factor)
parallelism-factor = 100.0
# Max number of threads to cap factor-based parallelism number to
parallelism-max = 5000
}
# Throughput defines the maximum number of messages to be
# processed per actor before the thread jumps to the next actor.
# Set to 1 for as fair as possible.
throughput = 500
}
在BatchActor中,我只是解析设备列表并将其提供给
class BatchActor(val workers:ActorRef, val config:BatchConfig) extends Actor
...
def receive = {
case Start => start
...
}
private def start = {
...
devices.map { devices =>
results(devices.host) = None
workers ! DeviceWork(self, config, devices, steps)
}
...
}
之后,WorkerActor将结果对象提交回BatchActer。
我的工作站:OS X - v10.9.3
java -version
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
生产机器:Red Hat Enterprise Linux Server 6.5版(圣地亚哥)
java -version
java version "1.7.0_65"
OpenJDK Runtime Environment (rhel-2.5.1.2.el6_5-x86_64 u65-b17)
OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)
软件:
Scala: v2.11.2
SBT: v0.13.6
Play: v2.3.5
Akka: v2.3.4
我使用typesafe activator / sbt来启动应用程序。命令如下:
cd <project dir>
./activator run -Dhttp.port=6600
任何帮助表示赞赏。我已经在这个问题上坚持了几天。
答案 0 :(得分:0)
我相信您的代码中有太多 parallelism ,即您在调度程序中创建了太多线程。您的Redhat框上有多少个核心?我从来没有见过这么高的价值。 FJ池中的许多线程可能导致大量上下文切换。尝试使用默认调度程序,看看是否能解决您的问题。您还可以将最小和最大并行度的值更改为您拥有的核心数的2或3倍。
fork-join-executor {
# Min number of threads to cap factor-based parallelism number to
parallelism-min = 1000
# Parallelism (threads) ... ceil(available processors * factor)
parallelism-factor = 100.0
# Max number of threads to cap factor-based parallelism number to
parallelism-max = 5000
}
要尝试的另一件事是使用(sbt-assembly)创建一个超级jar,然后部署它而不是使用激活器来部署它。
最后,您可以使用VisualJVM或Yourkit等内容查看JVM。
答案 1 :(得分:0)
花了几个小时尝试不同的事情,包括:
似乎没什么用。然后我进行了详细的比较,唯一不同的是我在笔记本电脑上使用了Oracle Hotspot实现,在生产机器上使用了OpenJDK实现。
所以我在生产机器上安装了Oracle VM,这似乎解决了这个问题。虽然我无法确定最终解决方案是什么,但似乎RHEL上的OpenJDK默认安装的编译或配置不同,不允许一次产生~500个线程。
我确定我错过了一些东西,但经过约3天的搜索后我找不到它。