在 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()
。
我正在使用加特林 运行 加载测试。我不是 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()
。