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 ='\\')
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。
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&desc=vacation_hawaii</loc> <changefreq>weekly</changefreq> </url> <url> <loc>http://www.example.com/catalog?item=73&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&desc=vacation_hawaii", "changefreq" -> "weekly") record3: Map( "loc" -> "http://www.example.com/catalog?item=73&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) } }