Multithreading 等待Scala的未来完成,然后继续下一个
我有一个500000个元素的列表和一个包含20个消费者的队列。消息以不同的速度处理(1、15、30、60秒;3、50分钟;3、16小时或更长。24小时是超时)。我需要消费者的回复,以便对数据进行一些处理。我将使用ScalaMultithreading 等待Scala的未来完成,然后继续下一个,multithreading,scala,akka-stream,Multithreading,Scala,Akka Stream,我有一个500000个元素的列表和一个包含20个消费者的队列。消息以不同的速度处理(1、15、30、60秒;3、50分钟;3、16小时或更长。24小时是超时)。我需要消费者的回复,以便对数据进行一些处理。我将使用ScalaFuture进行此操作,并使用基于事件的onComplete 为了不让队列泛滥,我想向队列发送前30条消息:20条消息将由消费者挑选,10条消息将在队列中等待。当一个Futures完成时,我想向队列发送另一条消息。你能给我一个实现这一目标的方法吗?这可以通过Akka Strea
Future
进行此操作,并使用基于事件的onComplete
为了不让队列泛滥,我想向队列发送前30条消息:20条消息将由消费者挑选,10条消息将在队列中等待。当一个Future
s完成时,我想向队列发送另一条消息。你能给我一个实现这一目标的方法吗?这可以通过Akka Streams实现吗
这是错误的,我只是想告诉你我想要什么:
private def sendMessage(ids: List[String]): Unit = {
val id = ids.head
val futureResult = Future {
//send id among some message to the queue
}.map { result =>
//process the response
}
futureResult.onComplete { _ =>
sendMessage(ids.tail)
}
}
def migrateAll(): Unit = {
val ids: List[String] = //get IDs from the DB
sendMessage(ids)
}
这是我用于此类任务的代码
class RateLimiter(semaphore: Semaphore) {
def runBlocking[T](action: => Future[T]): Future[T] = {
semaphore.acquire()
val started = try {
action
}
catch {
case NonFatal(th) => {
semaphore.release()
throw th
}
}
started.andThen {
case _ => semaphore.release()
}(ExecutionContext.Implicits.global)
}
}
val rateLimiter = new RateLimiter(new Semaphore(20))
val tasks = (1 to 100)
val futures: Seq[Future[Int]] = tasks.map(i => rateLimiter.runBlocking(Future{
i * 2
}(ExecutionContext.Implicits.global)))
futures.foreach(f => Await.result(f, Duration.Inf))
它不是完美的,因为它在两个位置阻塞(在信号量和“等待”中),并在内存中保存所有未来(可以避免)
但它可以在生产环境中使用:)下面是一个简单的例子,它使用Akka Streams为您的用例建模 让我们将处理定义为一个方法,该方法接受
字符串
,并返回未来[String]
:
def process(id: String): Future[String] = ???
然后,我们从一个包含500000个字符串元素的列表创建一个源,并使用它将元素提供给处理方法。并行级别设置为20,这意味着在任何时间点运行的Future
s不超过20。随着每个未来的完成,我们将执行附加处理并打印结果:
Source((1 to 500000).map(_.toString).toList)
.mapAsync(parallelism = 20)(process)
// do something with the result of the Future; here we create a new string
// that begins with "Processed: "
.map(s => s"Processed: $s")
.runForeach(println)
您可以在中阅读有关mapsync
的更多信息。使用Monix非常容易。akka streams非常适合您的场景,如果您已经对Scala有点熟悉(它还有一个JavaDSL),那么您很快就会了解它的DSL。Akka streams有内置的背压设施,应该注意不要淹没下游队列,但是如果您想要更细粒度地控制处理数据的速率,它也有节流设施-我应该记录更多关于信号量的信息,在某些情况下,它可能很有用。如果元素的下游顺序不重要,您可以使用mapAsyncUnordered
,mapAsync
对我来说是一个拦截器。