在 gatling 中使用 scala 迭代器时出现并发错误

Concurrency errors when using scala iterators in gatling

我正在使用加特林 运行 加载测试。我不是 gatling 或 scala 专家,但在我看来,这个特定问题可能与 scala(和 Akka)的关系比与 gatling 本身的关系更大。我将从最后开始:当 运行 在不太强大的机器上以高负载运行时,我偶尔会收到此错误:

07:04:42.071 [ERROR] i.g.c.a.b.SessionHookBuilder$$anonfun$build$$anon - 'sessionHook-29' crashed on session Session(searchOrder,5348761459522421680-8295,Map(),1433329482056,0, OK,List(),), forwarding to the next one java.lang.UnsupportedOperationException: tail of empty list at scala.collection.immutable.Nil$.tail(List.scala:422) ~[scala-library-2.11.6.jar:na] at scala.collection.immutable.Nil$.tail(List.scala:417) ~[scala-library-2.11.6.jar:na] at scala.collection.LinearSeqLike$$anon.next(LinearSeqLike.scala:46) ~[scala-library-2.11.6.jar:na] at scala.collection.TraversableOnce$FlattenOps$$anon.next(TraversableOnce.scala:450) ~[scala-library-2.11.6.jar:na] at MyNamespace.Tenants$.next(Tenants.scala:21) ~[na:na] at MyNamespace.LoadTests$$anonfun$run.apply(LoadTests.scala:43) ~[na:na] at MyNamespace.LoadTests$$anonfun$run.apply(LoadTests.scala:41) ~[na:na] at io.gatling.core.action.SessionHook.executeOrFail(SessionHook.scala:35) ~[gatling-core-2.1.5.jar:2.1.5] at io.gatling.core.action.Failable$class.execute(Actions.scala:71) ~[gatling-core-2.1.5.jar:2.1.5] at io.gatling.core.action.SessionHook.execute(SessionHook.scala:28) ~[gatling-core-2.1.5.jar:2.1.5] at io.gatling.core.action.Action$$anonfun$receive.applyOrElse(Actions.scala:29) ~[gatling-core-2.1.5.jar:2.1.5] at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:171) ~[scala-library-2.11.6.jar:na] at akka.actor.Actor$class.aroundReceive(Actor.scala:465) ~[akka-actor_2.11-2.3.9.jar:na] at io.gatling.core.akka.BaseActor.aroundReceive(BaseActor.scala:22) ~[gatling-core-2.1.5.jar:2.1.5] at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516) ~[akka-actor_2.11-2.3.9.jar:na] at akka.actor.ActorCell.invoke(ActorCell.scala:487) ~[akka-actor_2.11-2.3.9.jar:na] at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:254) ~[akka-actor_2.11-2.3.9.jar:na] at akka.dispatch.Mailbox.run(Mailbox.scala:221) ~[akka-actor_2.11-2.3.9.jar:na] at akka.dispatch.Mailbox.exec(Mailbox.scala:231) ~[akka-actor_2.11-2.3.9.jar:na] at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) ~[scala-library-2.11.6.jar:na] at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) ~[scala-library-2.11.6.jar:na] at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.6.jar:na] at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.6.jar:na]

我怀疑这与使用

有关
Iterator.continually([some collection]).flatten

其中 [some collection] 是我试图循环遍历的集合。会不会是当许多并发线程尝试访问以这种方式定义的迭代器时,它偶尔会失败?有没有更好的方法来创建线程安全的循环迭代器?

如下面"save"方法创建的租户对象(实际上这里有更多的代码填充get方法检索到的集合,所以它实际上永远不会为空):

import io.gatling.core.Predef._

object Tenants {
  type TenantKeys = Map[String, Map[String, String]]
  val key = "tenants"
  val elementKey = "tenant"
  var tenants: Iterator[Map[String, String]] = Iterator(Map[String, String]())

  def get(session: Session): TenantKeys = {
    session(key)
      .asOption[TenantKeys]
      .getOrElse(Map[String, Map[String, String]]())
  }

  def save(session: Session): Session = {
    val mapped = get(session).map(_._2).toSeq
    tenants = Iterator.continually(mapped).flatten
    session
  }
}

你所做的不能简单地起作用。这不是 Scala 的问题,而是并发编程的问题之一。 就像 Java 迭代器一样,Scala 的迭代器不是线程安全的。

使用线程安全的 Gatling feed()