diff --git a/.gitignore b/.gitignore index bef95b8d..216ced7d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ bin/ ### Mac OS ### .DS_Store + +### .env ### +.env diff --git a/Docker-compose.yml b/Docker-compose.yml new file mode 100644 index 00000000..edafb9ae --- /dev/null +++ b/Docker-compose.yml @@ -0,0 +1,56 @@ +services: + db: + image: "mysql:8.3.0" + container_name: load_test_mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root#1234 + MYSQL_DATABASE: load_test_db + TZ: UTC + + ports: + - "3000:3306" + deploy: + resources: + limits: + cpus: "0.5" + memory: "512MB" + + redis: + image: "docker.io/bitnami/redis:7.2" + container_name: load_test_redis + restart: always + environment: + - ALLOW_EMPTY_PASSWORD=yes + - REDIS_AOF_ENABLED=yes + - REDIS_RDB_ENABLED=no + ports: + - "6299:6379" + deploy: + resources: + limits: + cpus: "0.5" + memory: "512MB" + + teumteum-server: + build: + context: . + dockerfile: Dockerfile + restart: always + environment: + DB_URL: jdbc:mysql://db:3306/load_test_db + DB_USERNAME: root + DB_PASSWORD: root#1234 + REDIS_HOST: redis + REDIS_PORT: 6379 + JWT_SECERT_KEY: ${JWT_ACCESS_KEY} + + depends_on: + - db + - redis + ports: + - "8080:8080" + +networks: + teumteum_local: + driver: bridge diff --git a/src/gatling/java/protocol/Protocol.java b/src/gatling/java/protocol/Protocol.java index b131c75a..6f8fe8da 100644 --- a/src/gatling/java/protocol/Protocol.java +++ b/src/gatling/java/protocol/Protocol.java @@ -7,7 +7,7 @@ public class Protocol { private static final String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"; - public static final HttpProtocolBuilder httpProtocol = HttpDsl.http.baseUrl("https://api.teum.org") + public static final HttpProtocolBuilder httpProtocol = HttpDsl.http.baseUrl("http://localhost:8080") .header("Content-Type", "application/json") .userAgentHeader(USER_AGENT); diff --git a/src/gatling/java/simulation/SimulationSample.java b/src/gatling/java/simulation/SimulationSample.java index c5f90cfd..1c137d2d 100644 --- a/src/gatling/java/simulation/SimulationSample.java +++ b/src/gatling/java/simulation/SimulationSample.java @@ -16,8 +16,7 @@ public class SimulationSample extends Simulation { private final ScenarioBuilder scn = scenario(this.getClass().getSimpleName()) .exec(http("get user") .get("/users/1") - .check(status().is(200)) - ); + .check(status().is(200))); { setUp( diff --git a/src/gatling/java/simulation/TeumTeumApiSimulation.java b/src/gatling/java/simulation/TeumTeumApiSimulation.java new file mode 100644 index 00000000..b06f56bd --- /dev/null +++ b/src/gatling/java/simulation/TeumTeumApiSimulation.java @@ -0,0 +1,66 @@ +package simulation; + +import static io.gatling.javaapi.core.CoreDsl.StringBody; +import static io.gatling.javaapi.core.CoreDsl.constantUsersPerSec; +import static io.gatling.javaapi.core.CoreDsl.jsonPath; +import static io.gatling.javaapi.core.CoreDsl.rampUsersPerSec; +import static io.gatling.javaapi.core.CoreDsl.scenario; +import static io.gatling.javaapi.http.HttpDsl.http; +import static io.gatling.javaapi.http.HttpDsl.status; +import static protocol.Protocol.httpProtocol; + +import io.gatling.javaapi.core.ScenarioBuilder; +import io.gatling.javaapi.core.Simulation; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import net.datafaker.Faker; + +public class TeumTeumApiSimulation extends Simulation { + + private static final Faker faker = new Faker(); + private final ScenarioBuilder teumteumScn = scenario("TeumTeum 찬해지기 API 부하 테스트를 진행한다.") + .exec(session -> + session + .set("id", java.util.UUID.randomUUID().toString()) + .set("name", faker.name().fullName())) + + .exec(http("User 카드 등록 API 요청") + .post("/users") + .body(StringBody( + "{" + + "\"id\": \"${id}\", " + + "\"terms\": {\"service\": true, \"privatePolicy\": true}, " + + "\"name\": \"${name}\", " + + "\"birth\": \"20000402\", " + + "\"characterId\": 2, " + + "\"authenticated\": \"네이버\", " + + "\"activityArea\": \"경기 시흥\", " + + "\"mbti\": \"ENFP\", " + + "\"status\": \"직장인\", " + + "\"job\": {\"name\" : \"카카오 뱅크\", \"class\" : \"개발\", \"detailClass\" : \"BE 개발자\"}, " + + "\"interests\": [\"네트워킹\", \"IT\", \"모여서 각자 일하기\"], " + + "\"goal\": \"회사에서 좋은 사람들과 멋진 개발하기\"" + + "}" + )) + .check(status().is(201)) + .check(jsonPath("$.id").saveAs("userId")) + .check(jsonPath("$.accessToken").saveAs("accessToken")) + .check(jsonPath("$.refreshToken").saveAs("refreshToken"))) + + .exec(http("TeumTeum 친해지기 API 요청") + .post("/teum-teum/around") + .header("Authorization", "Bearer ${accessToken}") + .body(StringBody("{\"id\": ${userId}, \"latitude\": 37.5665, \"longitude\": 126.9780," + + " \"name\": \"test_name\", \"jobDetailClass\": \"test_job\", \"characterId\": 1}")) + .check(status().is(200)) + ); + + { + setUp( + teumteumScn.injectOpen( + constantUsersPerSec(10).during(Duration.of(30, ChronoUnit.SECONDS)), + rampUsersPerSec(10).to(50).during(Duration.of(30, ChronoUnit.SECONDS)) + ).protocols(httpProtocol) + ); + } +} diff --git a/src/gatling/java/simulation/UserApiSimulation.java b/src/gatling/java/simulation/UserApiSimulation.java new file mode 100644 index 00000000..9f7aceee --- /dev/null +++ b/src/gatling/java/simulation/UserApiSimulation.java @@ -0,0 +1,54 @@ +package simulation; + +import static io.gatling.javaapi.core.CoreDsl.StringBody; +import static io.gatling.javaapi.core.CoreDsl.atOnceUsers; +import static io.gatling.javaapi.core.CoreDsl.jsonPath; +import static io.gatling.javaapi.core.CoreDsl.scenario; +import static io.gatling.javaapi.http.HttpDsl.http; +import static io.gatling.javaapi.http.HttpDsl.status; +import static protocol.Protocol.httpProtocol; + +import io.gatling.javaapi.core.ScenarioBuilder; +import io.gatling.javaapi.core.Simulation; + +public class UserApiSimulation extends Simulation { + + private final ScenarioBuilder UserScn = scenario("User API 부하 테스트를 진행한다.") + .exec(http("User 카드 등록 API 요청") + .post("/users") + .body(StringBody( + "{\"id\": \"test_id\", " + + "\"terms\": {\"service\": true, \"privatePolicy\": true}, " + + "\"name\": \"홍길동\", " + + "\"birth\": \"1990-01-01\", " + + "\"characterId\": 1, " + + "\"authenticated\": \"SNS\", " + + "\"activityArea\": \"서울\", " + + "\"mbti\": \"INTJ\", " + + "\"status\": \"ACTIVE\", " + + "\"job\": {\"name\": \"개발자\", \"class\": \"IT\", \"detailClass\": \"백엔드\"}, " + + "\"interests\": [\"코딩\", \"독서\", \"운동\"], " + + "\"goal\": \"성장하기 위해 노력하는 개발자가 되기\"}" + )) + .check(status().is(201)) + .check(jsonPath("$.id").saveAs("userId")) + .check(jsonPath("$.accessToken").saveAs("accessToken")) + .check(jsonPath("$.refreshToken").saveAs("refreshToken")) + + ).exec(http("User 정보 조회 API 요청") + .get("/users/${userId}") + .header("Authorization", "Bearer ${accessToken}") + .check(status().is(200)) + + ).exec(http("User 리뷰 조회 API 요청") + .get("/users/${userId}") + .header("Authorization", "Bearer ${accessToken}") + .check(status().is(200))); + + + { + setUp( + UserScn.injectOpen( + atOnceUsers(10)).protocols(httpProtocol)); + } +}