Я познакомился с инструментом тестирования производительности с открытым исходным кодом Gatling несколько месяцев назад от Дастина Барнса и влюбился в него. Он имеет простой в использовании DSL, и хотя я не знаю, что такое Scala , я смог понять, как его использовать. Он создает довольно классную графику и выполняет за вас большую работу за кулисами. У них есть отличная документация и довольно активная группа Google, где приветствуются новички и вопросы.
Он поставляется вместе со Scala, поэтому все, что вам нужно сделать, это создать свои тесты и использовать командную строку для их выполнения. Я покажу вам, как сделать несколько основных вещей, например, проверить, что у вас все работает, затем мы создадим узлы и отношения и затем запросим эти узлы.
Мы начнем с операторов импорта:
import com.excilys.ebi.gatling.core.Predef._ import com.excilys.ebi.gatling.http.Predef._ import akka.util.duration._ import bootstrap._
Тогда мы начнем сразу с нашей симуляции. Для этого первого теста мы просто собираемся получить корневой узел через API REST. Мы указываем наш сервер Neo4j, в этом случае я тестирую на локальном хосте (вы захотите запустить тестовый код и сервер Neo4j на разных серверах, когда делаете это по-настоящему). Далее мы указываем, что мы принимаем JSON для возврата. В нашем тестовом сценарии в течение 10 секунд мы получим «/ db / data / node / 0» и проверим, что Neo4j возвращает код состояния http 200 (все будет в порядке). Мы сделаем паузу между 0 и 5 миллисекундами между вызовами, чтобы симулировать реальных пользователей, и в нашей настройке мы укажем, что нам нужно 100 пользователей.
class GetRoot extends Simulation { val httpConf = httpConfig .baseURL("http://localhost:7474") .acceptHeader("application/json") val scn = scenario("Get Root") .during(10) { exec( http("get root node") .get("/db/data/node/0") .check(status.is(200))) .pause(0 milliseconds, 5 milliseconds) } setUp( scn.users(100).protocolConfig(httpConf) ) }
Мы назовем этот файл «GetRoot.scala» и поместим его в user-files / simulations / neo4j.
gatling-charts-highcharts-1.4.0/user-files/simulations/neo4j/
Мы можем запустить наш код с:
~$ bin/gatling.sh
Мы получим подсказку с вопросом, какой тест мы хотим запустить:
GATLING_HOME is set to /Users/maxdemarzi/Projects/gatling-charts-highcharts-1.4.0 Choose a simulation number: [0] GetRoot [1] advanced.AdvancedExampleSimulation [2] basic.BasicExampleSimulation
Выберите номер рядом с GetRoot и нажмите ввод.
Затем вам будет предложено ввести идентификатор, или вы можете просто перейти по умолчанию, нажав клавишу ввода еще раз:
Select simulation id (default is 'getroot'). Accepted characters are a-z, A-Z, 0-9, - and _
Если вы хотите добавить описание, вы можете:
Select run description (optional)
Наконец это начинается по-настоящему:
================================================================================ 2013-02-14 17:18:03 10s elapsed ---- Get Root ------------------------------------------------------------------ Users : [#################################################################]100% waiting:0 / running:0 / done:100 ---- Requests ------------------------------------------------------------------ > get root node OK=58457 KO=0 ================================================================================ Simulation finished. Simulation successful. Generating reports... Reports generated in 0s. Please open the following file : /Users/maxdemarzi/Projects/gatling-charts-highcharts-1.4.0/results/getroot-20130214171753/index.html
Индикатор выполнения — это показатель общего числа пользователей, выполнивших свою задачу, а не показатель выполненного моделирования, поэтому не беспокойтесь, если он долгое время остается на нуле, а затем быстро переходит на 100%. Вы также можете увидеть номера OK (тест пройден) и KO (тест не пройден). Наконец, он создает отличный отчет на основе HTML для нас. Давайте взглянем:
Здесь вы можете увидеть статистику о времени ответа, а также количество запросов в секунду. Это здорово, мы можем получить корневой узел, но это не очень интересно, давайте создадим несколько узлов:
class CreateNodes extends Simulation { val httpConf = httpConfig .baseURL("http://localhost:7474") .acceptHeader("application/json") val createNode = """{"query": "create me"}""" val scn = scenario("Create Nodes") .repeat(1000) { exec( http("create node") .post("/db/data/cypher") .body(createNode) .asJSON .check(status.is(200))) .pause(0 milliseconds, 5 milliseconds) } setUp( scn.users(100).ramp(10).protocolConfig(httpConf) ) }
В этом случае мы устанавливаем 100 пользователей для создания 1000 узлов каждый со временем линейного изменения 10 секунд. Мы запустим эту симуляцию, как и раньше, но выберите «Создать узлы». Как только это будет сделано, взгляните на отчет и немного прокрутите вниз, чтобы увидеть график количества запросов в секунду:
Вы можете видеть количество пользователей, увеличивающихся за первые 10 секунд и исчезающих в конце. Давайте продолжим и соединим некоторые из этих узлов вместе:
Мы добавим JSONObject в операторы импорта, и, поскольку я хочу увидеть, какие узлы мы связываем с какими узлами вместе, мы распечатаем детали для запроса. Я случайно выбираю два идентификатора и передаю их в запрос на шифрование для создания отношений:
import com.excilys.ebi.gatling.core.Predef._ import com.excilys.ebi.gatling.http.Predef._ import akka.util.duration._ import bootstrap._ import util.parsing.json.JSONObject class CreateRelationships extends Simulation { val httpConf = httpConfig .baseURL("http://localhost:7474") .acceptHeader("application/json") .requestInfoExtractor(request => { println(request.getStringData) Nil }) val rnd = new scala.util.Random val chooseRandomNodes = exec((session) => { session.setAttribute("params", JSONObject(Map("id1" -> rnd.nextInt(100000), "id2" -> rnd.nextInt(100000))).toString()) }) val createRelationship = """START node1=node({id1}), node2=node({id2}) CREATE UNIQUE node1-[:KNOWS]->node2""" val cypherQuery = """{"query": "%s", "params": %s }""".format(createRelationship, "${params}") val scn = scenario("Create Relationships") .during(30) { exec(chooseRandomNodes) .exec( http("create relationships") .post("/db/data/cypher") .header("X-Stream", "true") .body(cypherQuery) .asJSON .check(status.is(200))) .pause(0 milliseconds, 5 milliseconds) } setUp( scn.users(100).ramp(10).protocolConfig(httpConf) ) }
Когда вы запустите это, вы увидите поток параметров, которые мы отправили на наш почтовый запрос:
{"query": "START node1=node({id1}), node2=node({id2}) CREATE UNIQUE node1-[:KNOWS]->node2", "params": {"id1" : 98468, "id2" : 20147} } {"query": "START node1=node({id1}), node2=node({id2}) CREATE UNIQUE node1-[:KNOWS]->node2", "params": {"id1" : 83557, "id2" : 26633} } {"query": "START node1=node({id1}), node2=node({id2}) CREATE UNIQUE node1-[:KNOWS]->node2", "params": {"id1" : 22386, "id2" : 99139} }
Вы можете отключить это, но я просто хотел убедиться, что идентификаторы были случайными, и это помогает при отладке. Теперь мы можем запросить график. Для этого следующего моделирования я хочу увидеть ответы, возвращенные из Neo4j, и я хочу видеть узлы, связанные с 10 случайными узлами, переданными в виде массива JSON. Обратите внимание, что это немного отличается от предыдущего, и мы также проверяем, получили ли мы «данные» обратно в нашем запросе.
import com.excilys.ebi.gatling.core.Predef._ import com.excilys.ebi.gatling.http.Predef._ import akka.util.duration._ import bootstrap._ import util.parsing.json.JSONArray class QueryGraph extends Simulation { val httpConf = httpConfig .baseURL("http://localhost:7474") .acceptHeader("application/json") .responseInfoExtractor(response => { println(response.getResponseBody) Nil }) .disableResponseChunksDiscarding val rnd = new scala.util.Random val nodeRange = 1 to 100000 val chooseRandomNodes = exec((session) => { session.setAttribute("node_ids", JSONArray.apply(List.fill(10)(nodeRange(rnd.nextInt(nodeRange length)))).toString()) }) val getNodes = """START nodes=node({ids}) MATCH nodes -[:KNOWS]-> other_nodes RETURN ID(other_nodes)""" val cypherQuery = """{"query": "%s", "params": {"ids": %s}}""".format(getNodes, "${node_ids}") val scn = scenario("Query Graph") .during(30) { exec(chooseRandomNodes) .exec( http("query graph") .post("/db/data/cypher") .header("X-Stream", "true") .body(cypherQuery) .asJSON .check(status.is(200)) .check(jsonPath("data"))) .pause(0 milliseconds, 5 milliseconds) } setUp( scn.users(100).ramp(10).protocolConfig(httpConf) ) }
Если мы посмотрим на вкладку сведений для этого моделирования, мы увидим небольшой всплеск в середине:
Это контрольный знак того, что сборка мусора в JVM происходит, и мы можем захотеть разобраться в этом. Отредактируйте файл neo4j / conf / neo4j-wrapper.conf и раскомментируйте ведение журнала сбора мусора, а также добавьте метки времени, чтобы улучшить видимость проблемы:
# Uncomment the following line to enable garbage collection logging wrapper.java.additional.4=-Xloggc:data/log/neo4j-gc.log wrapper.java.additional.5=-XX:+PrintGCDateStamps
Настройка производительности Neo4j заслуживает отдельного поста в блоге, но, по крайней мере, теперь у вас есть отличный способ проверить свою производительность, настроив JVM, кеш, оборудование, балансировку нагрузки и другие параметры. Не забывайте, что тестирование Neo4j напрямую — это круто, вы можете использовать Gatling для тестирования всего веб-приложения и измерения производительности от начала до конца.