gatling系列教程(翻译)-(附加二)feeders

Feeder是Iterator [Map [String,T]]的类型别名,这意味着由feed方法建立的组件将轮询Map [String,T]记录并注入其内容。html

创建一个定制的很是简单。 例如,下面是如何构建一个随机的电子邮件发生器:java

import scala.util.Random
val feeder = Iterator.continually(Map("email" -> (Random.alphanumeric.take(20).mkString + "@foo.com")))

这定义了一个工做流步骤,每一个虚拟用户都在同一个Feeder上提供。redis

每次虚拟用户达到此步骤时,它将从Feeder中弹出一条记录,该记录将被注入到用户的Session中,从而产生一个新的Session实例。sql

若是Feeder没法生成足够的记录,Gatling会抱怨它,您的模拟将中止。数据库

您也能够一次提供多条记录。 若是是,属性名称将被后缀。 例如,若是列是名称“foo”和“bar”,而且您一次提供2条记录,您将得到“foo1”,“bar1”,“foo2”和“bar2”会话属性json

feed(feeder, 2)

RecordSeqFeederBuilder
Array [Map [String,T]]或IndexedSeq [Map [String,T]]能够隐式地变成Feeder。 此外,这种隐式转换还提供了一些额外的方法来定义Seq迭代的方式:数组

.queue    // default behavior: use an Iterator on the underlying sequence
.random   // randomly pick an entry in the sequence
.shuffle  // shuffle entries, then behave live queue
.circular // go back to the top of the sequence once the end is reached

For example:session

val feeder = Array(
  Map("foo" -> "foo1", "bar" -> "bar1"),
  Map("foo" -> "foo2", "bar" -> "bar2"),
  Map("foo" -> "foo3", "bar" -> "bar3")).random

CSV feeder
Gatling提供了几个用于读取字符分隔值文件的内置函数。dom

文件预计将放置在Gatling发行版的数据目录中。 该位置能够被覆盖,请参阅配置。函数

默认状况下,咱们的解析器遵循RFC4180,所以不要期望不遵照此规范的行为。

惟一的区别是标题字段修剪包装空白。

val csvFeeder = csv("foo.csv") // use a comma separator
val tsvFeeder = tsv("foo.tsv") // //使用制表分隔符
val ssvFeeder = ssv("foo.ssv") // 使用分号分隔符
val customSeparatorFeeder = separatedValues("foo.txt", '#') // use your own separator

这些内置函数返回RecordSeqFeederBuilder实例,这意味着整个文件被加载到内存中并进行解析,所以在仿真运行期间,生成的引导程序不会在磁盘上读取。

警告

在内存中加载进feeder文件使用大量的堆,指望文件大小为5到10倍。 这是因为JVM的内部UTF-16字符编码和对象标头开销。 若是内存是一个问题,您可能须要从文件系统中即时读取并构建您本身的Feeder。

除了在RFC中描述的引用功能以外,还能够指定转义字符,所以某些内容字符不会被分隔符或引用字符混淆。

val csvFeeder = csv(“foo.csv”,escapeChar ='\\')

JSON feeders

Some might want to use data in JSON format instead of CSV:

val jsonFileFeeder = jsonFile("foo.json")
val jsonUrlFeeder = jsonUrl("http://me.com/foo.json")

For example, the following JSON:

[
  {
    "id":19434,
    "foo":1
  },
  {
    "id":19435,
    "foo":2
  }
]

will be turned into:

record1: Map("id" -> 19434, "foo" -> 1)
record2: Map("id" -> 19435, "foo" -> 2)

Note that the root element has of course to be an array.

请注意,根元素固然是一个数组。

JDBC feeder
Gatling还提供从JDBC链接读取的内置函数。

// beware: you need to import the jdbc module
import io.gatling.jdbc.Predef._

jdbcFeeder("databaseUrl", "username", "password", "SELECT * FROM users")

就像File parser的内置函数同样,这返回一个RecordSeqFeederBuilder实例。

databaseURL必须是JDBC URL(例如jdbc:postgresql:gatling),
用户名和密码是访问数据库的凭据,
sql是将获取所需值的查询。
仅支持JDBC4驱动程序,以便它们自动注册到DriverManager。

Sitemap Feeder

Gatling supports a feeder that reads data from a Sitemap file.

// beware: you need to import the http module
import io.gatling.http.Predef._

val feeder = sitemap("/path/to/sitemap/file")

The following Sitemap file:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>http://www.example.com/</loc>
    <lastmod>2005-01-01</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>

  <url>
    <loc>http://www.example.com/catalog?item=12&amp;desc=vacation_hawaii</loc>
    <changefreq>weekly</changefreq>
  </url>

  <url>
    <loc>http://www.example.com/catalog?item=73&amp;desc=vacation_new_zealand</loc>
    <lastmod>2004-12-23</lastmod>
    <changefreq>weekly</changefreq>
  </url>
</urlset>

will be turned into:

record1: Map(
           "loc" -> "http://www.example.com/",
           "lastmod" -> "2005-01-01",
           "changefreq" -> "monthly",
           "priority" -> "0.8")

record2: Map(
           "loc" -> "http://www.example.com/catalog?item=12&amp;desc=vacation_hawaii",
           "changefreq" -> "weekly")

record3: Map(
           "loc" -> "http://www.example.com/catalog?item=73&amp;desc=vacation_new_zealand",
           "lastmod" -> "2004-12-23",
           "changefreq" -> "weekly")

Redis feeder
这个功能最初由Krishnen Chedambarum提供。

Gatling能够使用如下Redis命令之一从Redis读取数据。

LPOP - 删除并返回列表的第一个元素
SPOP - 从集合中删除并返回一个随机元素
SRANDMEMBER - 从集合中返回一个随机元素
默认状况下RedisFeeder使用LPOP命令:

import com.redis._
import io.gatling.redis.feeder.RedisFeeder

val redisPool = new RedisClientPool("localhost", 6379)

// use a list, so there's one single value per record, which is here named "foo"
val feeder = RedisFeeder(redisPool, "foo")

可选的第三个参数用于指定所需的Redis命令:

// read data using SPOP command from a set named "foo"
val feeder = RedisFeeder(clientPool, "foo", RedisFeeder.SPOP)

请注意,自v2.1.14起,Redis支持从文件中大量插入数据。 Redis能够在几秒钟内加载数百万个密钥,Gatling将直接将内存从内存中读取。

例如:一个简单的Scala函数,用于生成一个具备100万个不一样URL的文件,能够在名为URLS的Redis列表中加载:

import java.io.{ File, PrintWriter }
import io.gatling.redis.util.RedisHelper._

def generateOneMillionUrls(): Unit = {
  val writer = new PrintWriter(new File("/tmp/loadtest.txt"))
  try {
    for (i <- 0 to 1000000) {
      val url = "test?id=" + i
      // note the list name "URLS" here
      writer.write(generateRedisProtocol("LPUSH", "URLS", url))
    }
  } finally {
    writer.close()
  }
}

The urls can then be loaded in Redis using the following command:

`cat /tmp/loadtest.txt | redis-cli --pipe`

转换
有时候,您可能须要转换您的进纸器的原始数据。

例如,一个csv进纸器只会给你一个字符串,但你可能但愿将一个属性转换成一个Int。

convert(convert:PartialFunction [(String,T),Any])须要:

一个PartialFunction,这意味着您只须要为要转换的范围定义它,则不匹配的属性将保持不变
其输入是(String,T)对,其中第一个元素是属性名称,第二个元素是属性值
而且其输出为Any,不管您想要什么
例如:

csv("myFile.csv").convert {
  case ("attributeThatShouldBeAnInt", string) => string.toInt
}

有时,您可能但愿全部虚拟用户都能播放文件中的全部记录,而Feeder与此行为不匹配。

不过,它很容易构建,感谢flattenMapIntoAttributes,例如:

val records = csv("foo.csv").records

foreach(records, "record") {
  exec(flattenMapIntoAttributes("${record}"))
}

用户依赖数据
有时,您可能须要根据会话中的某些信息过滤注入的数据。

馈线不能实现这一点,由于它只是一个迭代器,因此它不知道上下文。

而后,您必须编写本身的注入逻辑,但您固然能够重用Gatling解析器。

请考虑如下示例,其中有2个文件,而且要从第二个文件注入数据,具体取决于从第一个注入的内容。

In userProject.csv:

user, project
bob, aProject
sue, bProject

In projectIssue.csv:

project,issue
aProject,1
aProject,12
aProject,14
aProject,15
aProject,17
aProject,5
aProject,7
bProject,1
bProject,2
bProject,6
bProject,64

Here’s how you can randomly inject an issue, depending on the project:

import io.gatling.core.feeder._
import java.util.concurrent.ThreadLocalRandom

// index records by project
val recordsByProject: Map[String, IndexedSeq[Record[String]]] =
  csv("projectIssue.csv").records.groupBy{ record => record("project") }

// convert the Map values to get only the issues instead of the full records
val issuesByProject: Map[String, IndexedSeq[String]] =
  recordsByProject.mapValues{ records => records.map {record => record("issue")} }

// inject project
feed(csv("userProject.csv"))

  .exec { session =>
  // fetch project from  session
  session("project").validate[String].map { project =>

    // fetch project's issues
    val issues = issuesByProject(project)

    // randomly select an issue
    val selectedIssue = issues(ThreadLocalRandom.current.nextInt(issues.length))

    // inject the issue in the session
    session.set("issue", selectedIssue)
  }
}
相关文章
相关标签/搜索