1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-26 13:48:53 +08:00

MF-134 - Evaluate system's performance (#225)

* Add initial load tests

Add initial load tests for client creation and message publishing.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Make load tests executable as stand-alone application

Move code from test to main. Make code runnable with sbt run command.
Remove unnecessary config files.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Add native packager support

Add native packager plugin. Update sbt config to support native
packager. Update paths in Engine.scala.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Move files back to test folder and remove native packager support

Remove nativa packager plugin. Add gatling plugin and move files to
test folder where they belong. Read vars from JAVA_OPTS instead of
environment variables.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Add readme file for load tests

Add readme file for load tests with usage instructions.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Add number of requests per second as test parameter

Add number of requests per second as parameter. Update read me according
to this addition.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Add load test section in docs

Create documentation skeleton for load tests.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Reformat logger config file

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Update documentation skeleton

Move results section to scenarios. Move test environment to intro.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Align test version with mainflux version

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>

* Update mainflux version to 0.2.2

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
This commit is contained in:
Aleksandar Novaković 2018-04-23 15:13:43 +02:00 committed by Dejan Mijić
parent f449f8b9c8
commit 62297fedec
12 changed files with 287 additions and 1 deletions

23
docs/load-test.md Normal file
View File

@ -0,0 +1,23 @@
## Test scenarios
Testing environment to be determined.
### Message publishing
In this scenario, large number of requests are sent to HTTP adapter service
every second. This test checks how much time HTTP adapter took to response to
each request.
#### Results
TBD
### Create and get client
In this scenario, large number of requests are sent to manager service to create
client, and than to retrieve its data. This test checks how much time manager
service took to response to each request.
#### Results
TBD

8
load-test/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
target/
.classpath
.cache-tests
.cache-main
.settings/
.project
*.class
bin/

49
load-test/README.md Normal file
View File

@ -0,0 +1,49 @@
# Load Test
This SBT project contains load tests written for mainflux platform.
## Setup
In order to run load tests you must have [openjdk8](http://openjdk.java.net/install/) and [sbt](https://www.scala-sbt.org/1.0/docs/Setup.html) installed on your machine.
## Configuration
Tests are configured to use variables from `JAVA_OPTS` presented in the
following table. Note that any unset variables will be replaced with their
default values.
| Variable | Description | Default |
|----------|------------------------------------------|-----------------------|
| manager | Manager service URL | http://localhost:8180 |
| http | HTTP adapter service URL | http://localhost:8182 |
| requests | Number of requests to be sent per second | 100 |
## Usage
This project contains two simulations:
- `PublishSimulation`
- `CreateAndRetrieveClientSimulation`
To run all tests you will have to run following commands:
```bash
cd <path_to_mainflux_project>/load-test
sbt gatling:test
```
### Publish Simulation
`PublishSimulation` contains load tests for publishing messages. To run this test use following command:
```bash
sbt "gatling:testOnly com.mainflux.loadtest.simulations.PublishSimulation"
```
### Create And Retrieve Client Simulation
`CreateAndRetrieveClientSimulation` contains load tests for creating and retrieving clients. To run this test use following command:
```bash
sbt "gatling:testOnly com.mainflux.loadtest.simulations.CreateAndRetrieveClientSimulation"
```

18
load-test/build.sbt Normal file
View File

@ -0,0 +1,18 @@
enablePlugins(GatlingPlugin)
name := "load-test"
version := "0.2.2"
scalaVersion := "2.12.4"
val gatlingVersion = "2.3.1"
val circeVersion = "0.9.3"
libraryDependencies ++= Seq(
"io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion,
"io.gatling" % "gatling-test-framework" % gatlingVersion,
"org.scalaj" %% "scalaj-http" % "2.3.0",
"io.circe" %% "circe-core" % circeVersion,
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-parser" % circeVersion
)

View File

@ -0,0 +1 @@
sbt.version=1.1.2

View File

@ -0,0 +1 @@
addSbtPlugin("io.gatling" % "gatling-sbt" % "2.2.2")

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
</encoder>
<immediateFlush>false</immediateFlush>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="CONSOLE" />
</appender>
<root level="WARN">
<appender-ref ref="ASYNC" />
</root>
</configuration>

View File

@ -0,0 +1,7 @@
package com.mainflux.loadtest.simulations
object Constants {
val ManagerUrl = System.getProperty("manager", "http://localhost:8180")
val HttpAdapterUrl = System.getProperty("http", "http://localhost:8182")
val RequestsPerSecond = Integer.getInteger("requests", 100)
}

View File

@ -0,0 +1,65 @@
package com.mainflux.loadtest.simulations
import scala.concurrent.duration._
import scalaj.http.Http
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
import CreateAndRetrieveClientSimulation._
import io.gatling.http.protocol.HttpProtocolBuilder.toHttpProtocol
import io.gatling.http.request.builder.HttpRequestBuilder.toActionBuilder
import com.mainflux.loadtest.simulations.Constants._
class CreateAndRetrieveClientSimulation extends Simulation {
// Register user
Http(s"${ManagerUrl}/users")
.postData(User)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
// Login user
val tokenRes = Http(s"${ManagerUrl}/tokens")
.postData(User)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
.body
val tokenCursor = parse(tokenRes).getOrElse(Json.Null).hcursor
val token = tokenCursor.downField("token").as[String].getOrElse("")
// Prepare testing scenario
val httpProtocol = http
.baseURL(ManagerUrl)
.inferHtmlResources()
.acceptHeader("*/*")
.contentTypeHeader(ContentType)
.userAgentHeader("curl/7.54.0")
val scn = scenario("CreateAndGetClient")
.exec(http("CreateClientRequest")
.post("/clients")
.header(HttpHeaderNames.ContentType, ContentType)
.header(HttpHeaderNames.Authorization, token)
.body(StringBody(Client))
.check(status.is(201))
.check(headerRegex(HttpHeaderNames.Location, "(.*)").saveAs("location")))
.exec(http("GetClientRequest")
.get("${location}")
.header(HttpHeaderNames.Authorization, token)
.check(status.is(200)))
setUp(
scn.inject(
constantUsersPerSec(RequestsPerSecond.toDouble) during (15 second))).protocols(httpProtocol)
}
object CreateAndRetrieveClientSimulation {
val ContentType = "application/json; charset=utf-8"
val User = """{"email":"john.doe@email.com", "password":"123"}"""
val Client = """{"type":"device", "name":"weio"}"""
}

View File

@ -0,0 +1,98 @@
package com.mainflux.loadtest.simulations
import scala.concurrent.duration._
import scalaj.http.Http
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
import PublishSimulation._
import io.gatling.http.protocol.HttpProtocolBuilder.toHttpProtocol
import io.gatling.http.request.builder.HttpRequestBuilder.toActionBuilder
import com.mainflux.loadtest.simulations.Constants._
class PublishSimulation extends Simulation {
// Register user
Http(s"${ManagerUrl}/users")
.postData(User)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
// Login user
val tokenRes = Http(s"${ManagerUrl}/tokens")
.postData(User)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
.body
val tokenCursor = parse(tokenRes).getOrElse(Json.Null).hcursor
val token = tokenCursor.downField("token").as[String].getOrElse("")
// Register client
val clientLocation = Http(s"${ManagerUrl}/clients")
.postData(Client)
.header(HttpHeaderNames.Authorization, token)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
.headers.get("Location").get(0)
val clientId = clientLocation.split("/")(2)
// Get client key
val clientRes = Http(s"${ManagerUrl}/clients/${clientId}")
.header(HttpHeaderNames.Authorization, token)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
.body
val clientCursor = parse(clientRes).getOrElse(Json.Null).hcursor
val clientKey = clientCursor.downField("key").as[String].getOrElse("")
// Register channel
val chanLocation = Http(s"${ManagerUrl}/channels")
.postData(Channel)
.header(HttpHeaderNames.Authorization, token)
.header(HttpHeaderNames.ContentType, ContentType)
.asString
.headers.get("Location").get(0)
val chanId = chanLocation.split("/")(2)
// Connect client to channel
Http(s"${ManagerUrl}/channels/${chanId}/clients/${clientId}")
.method("PUT")
.header(HttpHeaderNames.Authorization, token)
.asString
// Prepare testing scenario
val httpProtocol = http
.baseURL(HttpAdapterUrl)
.inferHtmlResources()
.acceptHeader("*/*")
.contentTypeHeader("application/json; charset=utf-8")
.userAgentHeader("curl/7.54.0")
val scn = scenario("PublishMessage")
.exec(http("PublishMessageRequest")
.post(s"/channels/${chanId}/messages")
.header(HttpHeaderNames.ContentType, "application/senml+json")
.header(HttpHeaderNames.Authorization, clientKey)
.body(StringBody(Message))
.check(status.is(202)))
setUp(
scn.inject(
constantUsersPerSec(RequestsPerSecond.toDouble) during (15 second))).protocols(httpProtocol)
}
object PublishSimulation {
val ContentType = "application/json; charset=utf-8"
val User = """{"email":"john.doe@email.com", "password":"123"}"""
val Client = """{"type":"device", "name":"weio"}"""
val Channel = """{"name":"mychan"}"""
val Message = """[{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3}]"""
}

View File

@ -41,3 +41,4 @@ pages:
- License: LICENSE.txt
- Architecture: architecture.md
- Getting started: getting-started.md
- Load test: load-test.md

View File

@ -5,7 +5,7 @@ import (
"net/http"
)
const version string = "0.2.1"
const version string = "0.2.2"
type response struct {
Version string