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) } }