The NATS Gatling library provides a Gatling (an open-source load testing framework based on Scala, Akka and Netty) to NATS messaging system (a highly performant cloud native messaging system) Connector.
- Based on Gatling version 3.0.3.
- Based on Java Nats Streaming
2.1.2
, which includes NATS - Java Client version2.3.0
- scalaVersion := "2.12.8"
- Based on java-nats-streaming version 0.5.0
- Based on Gatling 2.2.5
- Introduces the NatsMessage Trait:
trait NatsMessage {
def getSubject(): String
def getPayload(): Array[Byte]
}
- Is based on Gatling version 2.2.2.
- Requires JDK8
- Is based on Gatling version 2.1.7.
To embed the NATS Gatling connector, add the following dependency to your project's Scala build.sbt
file.
libraryDependencies += "com.logimethods" % "nats-connector-gatling_2.11" % "1.0.0"
If you don't already have your build configured for using Maven releases from Sonatype / Nexus, you'll also need to add the following repository.
resolvers += "Sonatype OSS Release" at "https://oss.sonatype.org/content/groups/public/"
Snapshots are regularly uploaded to the Sonatype OSSRH (OSS Repository Hosting) using the same Maven coordinates.
If you are embedding the NATS Gatling connector, add the following dependency to your project's build.sbt
file.
libraryDependencies += "com.logimethods" %% "nats-connector-gatling_2.11" % "1.0.0-SNAPSHOT"
If you don't already have your build configured for using Maven snapshots, you'll also need to add the following repository.
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
...
import com.logimethods.connector.gatling.to_nats._
class NatsInjection extends Simulation {
val properties = new Properties()
properties.setProperty(io.nats.client.Constants.PROP_URL, "nats://localhost:4222")
val natsProtocol = NatsProtocol(properties, "GatlingSubject")
val natsScn = scenario("NATS call").exec(NatsBuilder("Message from Gatling!"))
setUp(
natsScn.inject(constantUsersPerSec(15) during (1 minute))
).protocols(natsProtocol)
}
import akka.actor.{ActorRef, Props}
import io.gatling.core.Predef._
import io.gatling.core.action.builder.ActionBuilder
import io.gatling.core.config.{Protocol, Protocols}
import scala.concurrent.duration._
import java.util.Properties
import io.nats.client.Constants.PROP_URL
import com.logimethods.connector.gatling.to_nats._
class NatsInjection extends Simulation {
val properties = new Properties()
// The URI of the NATS server is provided by an environment variable:
// >export NATS_URI=nats://nats-main:4222
println("System properties: " + System.getenv())
val natsUrl = System.getenv("NATS_URI")
properties.setProperty(io.nats.client.Constants.PROP_URL, natsUrl)
// The NATS Subject is also provided by an environment variable:
// >export GATLING.TO_NATS.SUBJECT=FROM_GATLING
var subject = System.getenv("GATLING.TO_NATS.SUBJECT")
if (subject == null) {
println("No Subject has been defined through the 'GATLING.TO_NATS.SUBJECT' Environment Variable!!!")
} else {
println("Will emit messages to " + subject)
val natsProtocol = NatsProtocol(properties, subject)
// The messages sent to NATS will not be constant thanks to the ValueProvider.
val natsScn = scenario("NATS call").exec(NatsBuilder(new ValueProvider()))
setUp(
natsScn.inject(constantUsersPerSec(15) during (1 minute))
).protocols(natsProtocol)
}
}
/**
* The ValueProvider will generate a loop of values: 100, 110, 120, 130, 140, 150, 100...
*/
class ValueProvider {
val incr = 10
val basedValue = 100 -incr
val maxIncr = 50
var actualIncr = 0
override def toString(): String = {
actualIncr = (actualIncr % (maxIncr + incr)) + incr
(basedValue + actualIncr).toString()
}
}
import scala.concurrent.duration._
import java.util.Properties
import io.nats.client.Constants.PROP_URL
import akka.actor.{ActorRef, Props}
import io.gatling.core.Predef._
import io.gatling.core.action.builder.ActionBuilder
import com.logimethods.connector.gatling.to_nats._
class NatsStreamingInjection extends Simulation {
val natsUrl = System.getenv("NATS_URI")
val clusterID = System.getenv("NATS_CLUSTER_ID")
var subject = System.getenv("GATLING_TO_NATS_SUBJECT")
if (subject == null) {
println("No Subject has been defined through the 'GATLING_TO_NATS_SUBJECT' Environment Variable!!!")
} else {
println("Will emit messages to " + subject)
val natsProtocol = NatsStreamingProtocol(natsUrl, clusterID, subject)
val natsScn = scenario("NATS call").exec(NatsStreamingBuilder(new ValueProvider()))
setUp(
natsScn.inject(constantUsersPerSec(15) during (1 minute))
).protocols(natsProtocol)
}
}
If you need to send NATS messages where the payload is different than String, or need to have non constant subjects, you have to provide NatsMessage
Objects to the NatsBuilder
.
import com.logimethods.connector.gatling.to_nats.NatsMessage
class MyMessage extends NatsMessage {
def getSubject(): String = {
return "MySubject"
}
def getPayload(): Array[Byte] = {
val value: Float = ...
var date:LocalDateTime = LocalDateTime.now()
// https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html
val buffer = ByteBuffer.allocate(8+4);
buffer.putLong(date.atOffset(ZoneOffset.MIN).toEpochSecond())
buffer.putFloat(value)
return buffer.array()
}
}
val natsScn = scenario("NATS call").exec(NatsBuilder(new MyMessage()))
- The 'docker-nats-connector-spark' Docker Based Project that makes use of Gatling, Spark & NATS.
- The 'smart-meter' Docker Swarm Based Project that makes use of Gatling, Cassandra, Golang, Spark & NATS.
(The MIT License)
Copyright (c) 2016 Logimethods.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.