diff --git a/README.md b/README.md
index 881f2e5..bb4c092 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,20 @@ All applications implement the same functionality and expose the same REST api
- `./run-micronaut.sh`
- `./run-quarkus.sh`
4. Run Gatling:
- - `./gradlew :loadtest:gatlingRun`
+ - `./gradlew :loadtest:gatlingRun-gatling.simulation.CommonSimulation`
+
+
+ Chec k wiremock performance
+
+Run Gradle
+```bash
+docker-compose up -d wiremock
+./gradlew :loadtest:gatlingRun-gatling.simulation.WiremockSimulation
+```
+
+
+
+
# Rest API
diff --git a/common-wiremock/src/main/resources/mockserver-config/expectationInitialiser.json b/common-wiremock/src/main/resources/mockserver-config/expectationInitialiser.json
new file mode 100644
index 0000000..9ff74f1
--- /dev/null
+++ b/common-wiremock/src/main/resources/mockserver-config/expectationInitialiser.json
@@ -0,0 +1,74 @@
+[
+ {
+ "httpRequest": {
+ "method": "GET",
+ "path": "/exchanges",
+ "queryStringParameters": {
+ "currency": "EUR"
+ }
+ },
+ "httpResponse": {
+ "statusCode": 200,
+ "body": {
+ "base": "EUR",
+ "rates": {
+ "USD": 1.0,
+ "EUR": 1.0,
+ "GBP": 0.5
+ }
+ },
+ "delay": {
+ "timeUnit": "MILLISECONDS",
+ "value": 200
+ }
+ }
+ },
+ {
+ "httpRequest": {
+ "method": "GET",
+ "path": "/exchanges",
+ "queryStringParameters": {
+ "currency": "GBP"
+ }
+ },
+ "httpResponse": {
+ "statusCode": 200,
+ "body": {
+ "base": "GBP",
+ "rates": {
+ "USD": 2.0,
+ "EUR": 2.0,
+ "GBP": 1.0
+ }
+ },
+ "delay": {
+ "timeUnit": "MILLISECONDS",
+ "value": 200
+ }
+ }
+ },
+ {
+ "httpRequest": {
+ "method": "GET",
+ "path": "/exchanges",
+ "queryStringParameters": {
+ "currency": "USD"
+ }
+ },
+ "httpResponse": {
+ "statusCode": 200,
+ "body": {
+ "base": "USD",
+ "rates": {
+ "USD": 1.0,
+ "EUR": 1.0,
+ "GBP": 0.5
+ }
+ },
+ "delay": {
+ "timeUnit": "MILLISECONDS",
+ "value": 200
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/common-wiremock/src/main/resources/stubs/__files/base-eur-response.json b/common-wiremock/src/main/resources/stubs/__files/base-eur-response.json
deleted file mode 100644
index 44dbae8..0000000
--- a/common-wiremock/src/main/resources/stubs/__files/base-eur-response.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "base": "EUR",
- "rates": {
- "USD": 1.0,
- "GBP": 0.5
- }
-}
\ No newline at end of file
diff --git a/common-wiremock/src/main/resources/stubs/__files/base-gbp-response.json b/common-wiremock/src/main/resources/stubs/__files/base-gbp-response.json
deleted file mode 100644
index 66a8143..0000000
--- a/common-wiremock/src/main/resources/stubs/__files/base-gbp-response.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "base": "GBP",
- "rates": {
- "USD": 2.0,
- "EUR": 2.0
- }
-}
\ No newline at end of file
diff --git a/common-wiremock/src/main/resources/stubs/__files/base-usd-response.json b/common-wiremock/src/main/resources/stubs/__files/base-usd-response.json
deleted file mode 100644
index 035c1c4..0000000
--- a/common-wiremock/src/main/resources/stubs/__files/base-usd-response.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "base": "USD",
- "rates": {
- "EUR": 1.0,
- "GBP": 0.5
- }
-}
\ No newline at end of file
diff --git a/common-wiremock/src/main/resources/stubs/mappings/get-eur-200.json b/common-wiremock/src/main/resources/stubs/mappings/get-eur-200.json
index dc0c59a..bbbf1f3 100644
--- a/common-wiremock/src/main/resources/stubs/mappings/get-eur-200.json
+++ b/common-wiremock/src/main/resources/stubs/mappings/get-eur-200.json
@@ -5,9 +5,17 @@
},
"response": {
"status": 200,
- "bodyFileName": "base-eur-response.json",
+ "jsonBody": {
+ "base": "EUR",
+ "rates": {
+ "USD": 1.0,
+ "EUR": 1.0,
+ "GBP": 0.5
+ }
+ },
"headers": {
"Content-Type": "application/json; charset=UTF-8"
- }
+ },
+ "fixedDelayMilliseconds": 200
}
}
\ No newline at end of file
diff --git a/common-wiremock/src/main/resources/stubs/mappings/get-gbp-200.json b/common-wiremock/src/main/resources/stubs/mappings/get-gbp-200.json
index 3105532..7ab5448 100644
--- a/common-wiremock/src/main/resources/stubs/mappings/get-gbp-200.json
+++ b/common-wiremock/src/main/resources/stubs/mappings/get-gbp-200.json
@@ -5,9 +5,17 @@
},
"response": {
"status": 200,
- "bodyFileName": "base-gbp-response.json",
+ "jsonBody": {
+ "base": "GBP",
+ "rates": {
+ "USD": 2.0,
+ "EUR": 2.0,
+ "GBP": 1.0
+ }
+ },
"headers": {
"Content-Type": "application/json; charset=UTF-8"
- }
+ },
+ "fixedDelayMilliseconds": 200
}
}
\ No newline at end of file
diff --git a/common-wiremock/src/main/resources/stubs/mappings/get-usd-200.json b/common-wiremock/src/main/resources/stubs/mappings/get-usd-200.json
index ac99f7a..6bab836 100644
--- a/common-wiremock/src/main/resources/stubs/mappings/get-usd-200.json
+++ b/common-wiremock/src/main/resources/stubs/mappings/get-usd-200.json
@@ -5,9 +5,17 @@
},
"response": {
"status": 200,
- "bodyFileName": "base-usd-response.json",
+ "jsonBody": {
+ "base": "USD",
+ "rates": {
+ "USD": 1.0,
+ "EUR": 1.0,
+ "GBP": 0.5
+ }
+ },
"headers": {
"Content-Type": "application/json; charset=UTF-8"
- }
+ },
+ "fixedDelayMilliseconds": 200
}
}
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 145a14c..3af1a31 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,7 @@
-version: '3.8'
+version: "3.8"
services:
db:
+ container_name: db
image: postgres:14.4
restart: always
environment:
@@ -8,13 +9,25 @@ services:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
ports:
- - '5432:5432'
+ - "5432:5432"
wiremock:
- image: wiremock/wiremock:2.32.0
+ container_name: wiremock
+ image: wiremock/wiremock:2.35.0
ports:
- "8888:8080"
- command:
- - "--global-response-templating"
+ command: ["--async-response-enabled=true"]
volumes:
- ./common-wiremock/src/main/resources/stubs:/home/wiremock
+
+ mockserver:
+ container_name: mockserver
+ image: mockserver/mockserver:5.15.0
+ ports:
+ - "8888:1080"
+ environment:
+ MOCKSERVER_LOG_LEVEL: WARN
+ MOCKSERVER_INITIALIZATION_JSON_PATH: /config/expectationInitialiser.json
+ MOCKSERVER_NIO_EVENT_LOOP_THREAD_COUNT: 20
+ volumes:
+ - ./common-wiremock/src/main/resources/mockserver-config:/config
diff --git a/dropwizard-app/src/test/java/bitxon/dropwizard/test/AbstractDropwizardTest.java b/dropwizard-app/src/test/java/bitxon/dropwizard/test/AbstractDropwizardTest.java
index 215f653..5ea3cb0 100644
--- a/dropwizard-app/src/test/java/bitxon/dropwizard/test/AbstractDropwizardTest.java
+++ b/dropwizard-app/src/test/java/bitxon/dropwizard/test/AbstractDropwizardTest.java
@@ -23,7 +23,7 @@ abstract class AbstractDropwizardTest {
.withDatabaseName("testdb")
.withUsername("postgres")
.withPassword("postgres")
- .withInitScript("sql/db-test-data.sql");
+ .withCopyFileToContainer(MountableFile.forClasspathResource("sql/db-test-data.sql"), "/docker-entrypoint-initdb.d/");
static GenericContainer WIREMOCK = new GenericContainer("wiremock/wiremock:2.35.0")
.withExposedPorts(8080)
.withCopyFileToContainer(MountableFile.forClasspathResource("stubs"), "/home/wiremock")
diff --git a/loadtest/src/gatling/java/gatling/simulation/CommonSimulation.java b/loadtest/src/gatling/java/gatling/simulation/CommonSimulation.java
index b85a7e5..28aa825 100644
--- a/loadtest/src/gatling/java/gatling/simulation/CommonSimulation.java
+++ b/loadtest/src/gatling/java/gatling/simulation/CommonSimulation.java
@@ -137,29 +137,37 @@ private static ChainBuilder postTransfer() {
{
setUp(
- scenarioGetAll.injectOpen(
- constantUsersPerSec(4).during(90)
- ),
- scenarioGetOne.injectOpen(
- nothingFor(4), // Wait
- atOnceUsers(10), // 10
- rampUsers(10).during(5), // 10 -> 20
- constantUsersPerSec(20).during(15), // 20
- constantUsersPerSec(20).during(15).randomized(), // 20
- rampUsersPerSec(10).to(20).during(10), // 10 -> 20
- rampUsersPerSec(10).to(20).during(10).randomized(), // 10 -> 20
- stressPeakUsers(400).during(20) // 8
- ),
- scenarioTransfer.injectOpen(
- incrementUsersPerSec(3)
- .times(6)
- .eachLevelLasting(8)
- .separatedByRampsLasting(8)
- .startingFrom(8)
- ),
- scenarioValidation.injectOpen(
- constantUsersPerSec(10).during(90)
+ scenarioTransfer.injectClosed(
+ constantConcurrentUsers(1000).during(10)
)
).protocols(httpProtocol);
}
+
+// {
+// setUp(
+// scenarioGetAll.injectOpen(
+// constantUsersPerSec(4).during(90)
+// ),
+// scenarioGetOne.injectOpen(
+// nothingFor(4), // Wait
+// atOnceUsers(10), // 10
+// rampUsers(10).during(5), // 10 -> 20
+// constantUsersPerSec(20).during(15), // 20
+// constantUsersPerSec(20).during(15).randomized(), // 20
+// rampUsersPerSec(10).to(20).during(10), // 10 -> 20
+// rampUsersPerSec(10).to(20).during(10).randomized(), // 10 -> 20
+// stressPeakUsers(400).during(20) // 8
+// ),
+// scenarioTransfer.injectOpen(
+// incrementUsersPerSec(3)
+// .times(6)
+// .eachLevelLasting(8)
+// .separatedByRampsLasting(8)
+// .startingFrom(8)
+// ),
+// scenarioValidation.injectOpen(
+// constantUsersPerSec(10).during(90)
+// )
+// ).protocols(httpProtocol);
+// }
}
diff --git a/loadtest/src/gatling/java/gatling/simulation/WiremockSimulation.java b/loadtest/src/gatling/java/gatling/simulation/WiremockSimulation.java
new file mode 100644
index 0000000..6c26f75
--- /dev/null
+++ b/loadtest/src/gatling/java/gatling/simulation/WiremockSimulation.java
@@ -0,0 +1,55 @@
+package gatling.simulation;
+
+import io.gatling.javaapi.core.ChainBuilder;
+import io.gatling.javaapi.core.FeederBuilder;
+import io.gatling.javaapi.core.ScenarioBuilder;
+import io.gatling.javaapi.core.Simulation;
+import io.gatling.javaapi.http.HttpProtocolBuilder;
+
+import static io.gatling.javaapi.core.CoreDsl.*;
+import static io.gatling.javaapi.http.HttpDsl.http;
+import static io.gatling.javaapi.http.HttpDsl.status;
+
+public class WiremockSimulation extends Simulation {
+
+ static final String BASE_URL = System.getProperty("baseUrl", "http://localhost:8888");
+ static final int USERS = Integer.getInteger("users", 800);
+ static final int REQUESTS_PER_USER = Integer.getInteger("requestsPerUser", 20);
+ static final int DURATION = Integer.getInteger("duration", 20);
+
+ //-----------------------------------------------------------------------------------------------------------------
+
+ static FeederBuilder feederCurrency = csv("feeders/currency.csv").random();
+
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private static ChainBuilder getExchangeRate() {
+ return exec(http("Get Exchange Rate")
+ .get("/exchanges?currency=#{currency}")
+ .check(status().is(200))
+ );
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+
+ ScenarioBuilder scenarioGetOne = scenario("Get One - Scenario")
+ .feed(feederCurrency)
+ .exec(
+ repeat(REQUESTS_PER_USER).on(getExchangeRate())
+ );
+
+ //-----------------------------------------------------------------------------------------------------------------
+
+ HttpProtocolBuilder httpProtocol = http.baseUrl(BASE_URL)
+ .acceptHeader("application/json")
+ .acceptLanguageHeader("en-US,en;q=0.5");
+
+ {
+ setUp(
+ scenarioGetOne.injectOpen(
+ rampUsers(USERS).during(DURATION)
+ )
+ ).protocols(httpProtocol);
+ }
+
+}
diff --git a/loadtest/src/gatling/java/gatling/utils/RandomUtils.java b/loadtest/src/gatling/java/gatling/utils/RandomUtils.java
new file mode 100644
index 0000000..33401bb
--- /dev/null
+++ b/loadtest/src/gatling/java/gatling/utils/RandomUtils.java
@@ -0,0 +1,13 @@
+package gatling.utils;
+
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class RandomUtils {
+
+
+ public static String peekRandom(List list) {
+ var rand = ThreadLocalRandom.current();
+ return list.get(rand.nextInt(list.size()));
+ }
+}
diff --git a/loadtest/src/gatling/resources/feeders/currency.csv b/loadtest/src/gatling/resources/feeders/currency.csv
new file mode 100644
index 0000000..b22f401
--- /dev/null
+++ b/loadtest/src/gatling/resources/feeders/currency.csv
@@ -0,0 +1,4 @@
+currency
+USD
+EUR
+GBP
\ No newline at end of file
diff --git a/micronaut-app/src/test/java/bitxon/micronaut/test/AbstractMicronautTest.java b/micronaut-app/src/test/java/bitxon/micronaut/test/AbstractMicronautTest.java
index 1e28919..a76b0b1 100644
--- a/micronaut-app/src/test/java/bitxon/micronaut/test/AbstractMicronautTest.java
+++ b/micronaut-app/src/test/java/bitxon/micronaut/test/AbstractMicronautTest.java
@@ -43,6 +43,7 @@ abstract class AbstractMicronautTest implements TestPropertyProvider {
@Override
public Map getProperties() {
return Map.of(
+ "micronaut.server.port", "${random.port}",
"datasources.default.url", DB.getJdbcUrl(),
"datasources.default.username", DB.getUsername(),
"datasources.default.password", DB.getPassword(),
diff --git a/spring-app/build.gradle b/spring-app/build.gradle
index 6e9a1e6..41da5f9 100644
--- a/spring-app/build.gradle
+++ b/spring-app/build.gradle
@@ -33,6 +33,7 @@ dependencies {
compileOnly("org.projectlombok:lombok")
runtimeOnly 'org.postgresql:postgresql'
+ //runtimeOnly 'io.netty:netty-resolver-dns-native-macos:4.1.92.Final:osx-aarch_64'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
diff --git a/test-mockserver.sh b/test-mockserver.sh
new file mode 100755
index 0000000..178369e
--- /dev/null
+++ b/test-mockserver.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+docker-compose down
+docker-compose up -d mockserver
+
+#Warm Up
+sleep 10
+for run in {1..3}; do
+ curl --location 'http://localhost:8888/exchanges?currency=USD'
+ curl --location 'http://localhost:8888/exchanges?currency=EUR'
+ curl --location 'http://localhost:8888/exchanges?currency=GBP'
+done
+
+#Test
+sleep 3
+./gradlew :loadtest:gatlingRun-gatling.simulation.WiremockSimulation
diff --git a/test-wiremock.sh b/test-wiremock.sh
new file mode 100755
index 0000000..37ad376
--- /dev/null
+++ b/test-wiremock.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+docker-compose down
+docker-compose up -d wiremock
+
+#Warm Up
+sleep 10
+for run in {1..3}; do
+ curl --location 'http://localhost:8888/exchanges?currency=USD'
+ curl --location 'http://localhost:8888/exchanges?currency=EUR'
+ curl --location 'http://localhost:8888/exchanges?currency=GBP'
+done
+
+#Test
+sleep 3
+./gradlew :loadtest:gatlingRun-gatling.simulation.WiremockSimulation