Scala 使用Akka角色时发生OutOfMemoryError
我有一个使用RabbitMQ消息的应用程序,我正在使用参与者来处理这项工作 我的做法如下:Scala 使用Akka角色时发生OutOfMemoryError,scala,rabbitmq,akka,actor,Scala,Rabbitmq,Akka,Actor,我有一个使用RabbitMQ消息的应用程序,我正在使用参与者来处理这项工作 我的做法如下: object QueueConsumer extends Queue { def consumeMessages = { setupListener(buildChannel(resultsQueueName), resultsQueueName, resultsCallback) } private def setupListener(receivingChann
object QueueConsumer extends Queue {
def consumeMessages = {
setupListener(buildChannel(resultsQueueName), resultsQueueName,
resultsCallback)
}
private def setupListener(receivingChannel: Channel, queue: String,
f: (String) => Any) {
Akka.system.scheduler.scheduleOnce(Duration(10, TimeUnit.SECONDS),
Akka.system.actorOf(Props(new QueueActor(receivingChannel, queue, f))), "")
}
}
class QueueActor(channel:Channel, queue:String, f:(String) => Any) extends Actor{
def receive = {
case _ => startReceiving
}
def startReceiving = {
val consumer = new QueueingConsumer(channel)
channel.basicConsume(queue, false, consumer)
while (true) {
val delivery = consumer.nextDelivery()
val msg = new String(delivery.getBody())
context.actorOf(Props(new Actor {
def receive = {
case some: String => f(some)
}
})) ! msg
channel.basicAck(delivery.getEnvelope.getDeliveryTag, false)
}
}
}
运行几秒钟后,它抛出一个java.lang.OutOfMemoryError:超出了GC开销限制
我认为这是因为我为收到的每一条消息都要开始一个新的参与者——所以如果我有100000条消息,它将创建100000个参与者。这是一个好方法还是应该实现类似“参与者池”的东西
有人知道如何在我的场景中避免OutOfMemory错误吗
先谢谢你
编辑1:
改变方法:
class Queue2(json:String) extends Actor {
def receive = {
case x: String =>
val envelope = MessageEnvelopeParser.toObject(x)
val processor = ProcessQueueServiceFactory.getProcessResultsService()
envelope.messages.foreach(message => processor.process(message))
}
}
object Queue2 {
def props(json: String): Props = Props(new Queue2(json))
}
class QueueActor(channel:Channel, queue:String) extends Actor {
def receive = {
case _ => startReceiving
}
def startReceiving = {
val consumer = new QueueingConsumer(channel)
channel.basicConsume(queue, false, consumer)
while (true) {
val delivery = consumer.nextDelivery()
val msg = new String(delivery.getBody())
context.actorOf(Queue2.props(msg))
channel.basicAck(delivery.getEnvelope.getDeliveryTag, false)
}
}
}
完成后,您的每封邮件的参与者将需要停止自己,否则他们将永远留在您身边。请参阅和上的文档。在这里,您只需添加
上下文。在处理完成后停止(self)
。为每条消息创建一个新的参与者
并不一定是糟糕的设计。这不应该让你的记忆耗尽,除非你以某种方式保留着演员或消息的每一个引用,这样GCer就无法收集它们。@Samuel我怎么能检查它?当actor调用回调函数(f)时,它会在sqlserver上执行一些查询(比如INSERT/SELECT,但都是简单的命令)。当回调完成时,是否需要实现一些东西来“杀死”参与者?我是一个新使用Actors的人。看起来Akka保留了您传递给actorOf()
的每个actor的引用。因此,似乎不应该为每个排队的消息创建新的参与者。我认为您应该有一个actor来执行SQL命令,并让您的QueueActor
从队列中读取命令并将命令发送给SQL执行器actor。如果你真的想做你现在正在做的事情,你可以使用Akka.system.stop()杀死一个演员引用。@Samuel我尝试过另一种方法,但同样的错误。你能检查一下我在这个主题上所做的这个版本,并检查它是否是你所说的吗?你有没有尝试将嵌套的Actor
放在QueueActor
传递f:(String=>Any)
ass构造函数参数之外?我似乎记得有人说过,通过闭包将一个参与者
的字段暴露给另一个参与者
是个坏主意。另外,(只是一个预感),而不是在代码中阻塞.>接收< /代码>,您可以考虑使用未来来接收消息并在代码>未来中创建消息角色。