티스토리 뷰

향해99

장애 대응.. +부하테스트

dev_0hoon 2024. 8. 20. 00:04

향해99 최종장으로써 장애대응을 파트를 진행하려 한다. 대부분이 부하 테스트의 목적이 있는 것 같은데, 4가지의 테스트가 있다.

 

 

Load Test (부하 테스트)

- 시스템이 예상되는 부하를 정상적으로 처리할 수 있는지 평가

- 특정한 부하를 제한된 시간 동안 제공해 이상이 없는지 파악

- 목표치를 설정해 적정한 Application 배포 Spec 또한 고려해 볼 수 있음

 

Endurance Test( 내구성 테스트 )

- 시스템이 장기간 동안 안정적으로 운영될 수 있는지 평가

- 특정한 부하를 장기간 동안 제공했을 때, 발생하는 문제가 있는지 파악

- 장기적으로 Application을 운영할 때 발생할 수 있는 숨겨진 문제를 파악해 볼 수 있음 (feat. Memory Leak, Slow Query 등 )

 

Stress Test(스트레스 테스트)

- 시스템이 지속적으로 증가하는 부하를 얼마나 잘 처리할 수 있는지 평가

- 점진적으로 부하를 증가시켰을 때, 발생하는 문제가 있는지 파악

- 장기적으로 Application을 운영하기 위한 Spec 및 확장성과 장기적인 운영 계획을 파악해 볼 수 있음

 

Peak Test ( 최고 부하 테스트)

- 시스템에 일시적으로 많은 부하가 가해졌을 때, 잘 처리하는지 평가

- 목표치로 설정한 임계 부하를 일순간에 제공했을 때, 정상적으로 처리해내는지 파악

- 선착순 이벤트 등을 준비하면서 정상적으로 서비스를 제공할 수 있을지 파악해 볼 수 있음

 

위의 4개의 테스트가 있으며, api의 특성에 따라 어떤 테스트를 할지 결정하게 된다.

 

🕰️ 0. 예상 TPS는?

TPS(Transaction Per Second)는 IT 시스템에서 초당 처리할 수 있는 트랜잭션의 수를 의미하는 성능 지표이다.

TPS의 주요 개념

  1. 트랜잭션: 트랜잭션은 데이터베이스 상에서 하나의 단위로 처리되는 작업입니다. 예를 들어, 은행에서 계좌 이체를 하는 경우, 금액 차감과 금액 입금이 하나의 트랜잭션으로 간주됩니다.
  2. 초당 처리 건수: TPS는 초당 처리되는 트랜잭션의 수를 의미합니다. 예를 들어, 1초에 100개의 트랜잭션이 처리된다면 이 시스템의 TPS는 100이 됩니다.
  3. TPS 측정: TPS는 일반적으로 부하 테스트 툴을 사용하여 측정합니다. 예를 들어, JMeter나 Gatling과 같은 툴을 사용하면 시스템에 인위적으로 부하를 주어 초당 처리할 수 있는 트랜잭션의 수를 측정할 수 있습니다.

예상 TPS란?

TPS= 100×5/60 =8.33 TPS

 

 

  • > (10초에 1,000명씩은 좌석 볼 것이다. 새로고침 명당 5번 기준 ) * 6
    > (1,000*5)*6/60 = 500TPS

  • /payment
    > (10초에 1,000명씩은 예약을 할 것이다. 새로고침 명당 2번 기준 ) * 6
    > (1,000*2)*6/60 = 200TPS

포인트

  • /wallet/balane/info
    > (10초에 1,000명씩은 포인트 조회. 새로고침 명당 2번 기준 ) * 6
    > (1,000*2)*6/60 = 200TPS
  • /wallet/balane/info
    > (10초에 1,000명씩은 포인트 충전. 새로고침 명당 1.5번 기준 ) * 6
    > (1,000*1.5)*6/60 = 150TPS

 

🏋️‍♀️ API 테스트

 

1) 동시 접속자 수 처리 능력

> (10초에 1,000명씩은 좌석 볼 것이다. 새로고침 명당 5번 기준 ) * 6
> (1,000*5)*6/60 = 500TPS

1단계 무작정 시작해보기

import http from 'k6/http';
import { check } from 'k6';

export const options = {
    stages: [
        { duration: '10s', target: 300 }, 
        { duration: '10s', target: 3000 }, 
        { duration: '10s', target: 300 }, 
        { duration: '10s', target: 5000 }, 
        { duration: '10s', target: 200 }, 
        { duration: '10s', target: 0 },    // 10초 동안 사용자 수를 0으로 줄임 (테스트 종료)
    ],
};

export default function () {
    const url = 'http://localhost:8080/reservation/available/seat?concertScheduleId=1'; // 쿼리 파라미터로 전달

    // API 호출
    const response = http.get(url);

    // 응답이 200 OK인지 확인
    check(response, {
        '예약 가능 일자': (res) => res.status === 200,
    });

    // 응답 데이터 출력
    console.log(`Response body ${__VU}: `, response.body);
}

 

     checks.........................: 100.00% ✓ 48478      ✗ 0     
     data_received..................: 14 MB   225 kB/s
     data_sent......................: 6.1 MB  102 kB/s
     http_req_blocked...............: avg=180.65µs min=1µs    med=4µs      max=128.89ms p(90)=310.3µs p(95)=537µs 
     http_req_connecting............: avg=125.82µs min=0s     med=0s       max=94.34ms  p(90)=215µs   p(95)=363µs 
     http_req_duration..............: avg=1.34s    min=1.43ms med=2.68ms   max=13.87s   p(90)=8.51s   p(95)=11.91s
       { expected_response:true }...: avg=1.34s    min=1.43ms med=2.68ms   max=13.87s   p(90)=8.51s   p(95)=11.91s
     http_req_failed................: 0.00%   ✓ 0          ✗ 48478 
     http_req_receiving.............: avg=4ms      min=14µs   med=120µs    max=5.49s    p(90)=2.29ms  p(95)=8.71ms
     http_req_sending...............: avg=303.75µs min=5µs    med=16µs     max=1.17s    p(90)=91µs    p(95)=162µs 
     http_req_tls_handshaking.......: avg=0s       min=0s     med=0s       max=0s       p(90)=0s      p(95)=0s    
     http_req_waiting...............: avg=1.34s    min=1.29ms med=2.46ms   max=13.87s   p(90)=8.5s    p(95)=11.88s
     http_reqs......................: 48478   807.260677/s
     iteration_duration.............: avg=2.56s    min=1.71ms med=970.49ms max=18.34s   p(90)=11.33s  p(95)=15.44s
     iterations.....................: 48478   807.260677/s
     vus............................: 12      min=12       max=4983
     vus_max........................: 5000    min=5000     max=5000

 

  •   http_req_duration : 요청 응답 시간을 보면 평균 1.34.초가 걸렸고 90%의 요청이 8.51초 이내에 완료되었으며, 이는 매우 긴 응답 시간이다.
  • http_req_waiting (서버에서 응답을 기다리는 시간) : 평균 대기 시간이 1.34초로, 긴 시간이다.

너무 느리다. 개선이 필요 할 것 같다. 

 

해결

api 내에서 사용하는 유일한 쿼리이다. 조회 쿼리로 JPQL상으로는 문제 없어 보인다.

    @Query("select s from ConcertSeat s where s.concertSchedule.id = :concertScheduleId and s.status = :status")
    List<ConcertSeat> findAllByConcertScheduleIdAndStatus(@Param("concertScheduleId") Long concertScheduleId, @Param("status")SeatStatus status);

 

2024-08-22T19:50:25.685+09:00 DEBUG 1314 --- [concert] [nio-8080-exec-2] org.hibernate.SQL                        : 
    select
        cs1_0.concert_seat_id,
        cs1_0.assignment_time,
        cs1_0.concert_schedule_id,
        cs1_0.seat,
        cs1_0.status,
        cs1_0.temp_assignment_time,
        cs1_0.user_id,
        cs1_0.version 
    from
        concert_seat cs1_0 
    where
        cs1_0.concert_schedule_id=? 
        and cs1_0.status=?
2024-08-22T19:50:25.707+09:00 TRACE 1314 --- [concert] [nio-8080-exec-2] org.hibernate.orm.jdbc.bind              : binding parameter (1:BIGINT) <- [1]
2024-08-22T19:50:25.708+09:00 TRACE 1314 --- [concert] [nio-8080-exec-2] org.hibernate.orm.jdbc.bind              : binding parameter (2:ENUM) <- [UNASSIGNED]
2024-08-22T19:50:25.715+09:00 DEBUG 1314 --- [concert] [nio-8080-exec-2] org.hibernate.SQL                        : 
    select
        cs1_0.concert_schedule_id,
        c1_0.concert_id,
        c1_0.concert_at,
        c1_0.concert_title,
        c1_0.description,
        c1_0.version,
        cs1_0.concert_at,
        cs1_0.create_at,
        cs1_0.price,
        cs1_0.total_available,
        cs1_0.update_at,
        cs1_0.version 
    from
        concert_schedule cs1_0 
    left join
        concert c1_0 
            on c1_0.concert_id=cs1_0.concert_id 
    where
        cs1_0.concert_schedule_id=?

하지만 호출하면 두개의 쿼리가 날아간다. 만약 ConcertSeat 엔티티의 ConcertSchedule 속성이 EAGER 로딩으로 설정되어 있다면, Hibernate는 기본적으로 해당 엔티티를 즉시 로딩하려고 시도한다.

 

fetch를 LAZY로 설정해준 뒤에 확인해보면

    select
        cs1_0.concert_seat_id,
        cs1_0.assignment_time,
        cs1_0.concert_schedule_id,
        cs1_0.seat,
        cs1_0.status,
        cs1_0.temp_assignment_time,
        cs1_0.user_id,
        cs1_0.version 
    from
        concert_seat cs1_0 
    where
        cs1_0.concert_schedule_id=? 
        and cs1_0.status=?

 

쿼리를 한번만 호출 한다.

성능 지표 비교

지표처리 전처리 후비교

총 요청 수 (http_reqs) 48,478 100,553 증가 (약 2배)
초당 요청 수 (http_reqs/s) 807.26/s 1,675.72/s 증가 (약 2배)
평균 응답 시간 (http_req_duration) 1.34초 510ms 감소 (성능 개선)
최소 응답 시간 1.43ms 455µs 감소 (개선)
중앙값 응답 시간 (med) 2.68ms 1.94ms 감소 (개선)
90번째 백분위수 (p(90)) 8.51초 2.88초 감소 (개선)
95번째 백분위수 (p(95)) 11.91초 3.99초 감소 (개선)
최대 응답 시간 (max) 13.87초 5.72초 감소 (개선)
평균 수신 시간 (http_req_receiving) 4ms 1.87ms 감소 (개선)
평균 대기 시간 (http_req_waiting) 1.34초 508.1ms 감소 (개선)
평균 전송 시간 (http_req_sending) 303.75µs 23.77µs 감소 (개선)
최대 사용자 수 (vus_max) 5000 5000 변화 없음
평균 반복 실행 시간 (iteration_duration) 2.56초 967.66ms 감소 (개선)

 

쿼리를 바꿔주는 것만으로도 성능이 많이 개선되었다. 요청 수가 늘어난 반면 모든 부분에서 개선되었다.

 

1,675TPS로 필요로 했던 200TPS보다는 굉장히 빠른 속도이다.

 

 

Enduration Test

- 해당 부분은 일정하게 사용자를 받을 수 있는 부분이다. 그에 맞게 테스트를 진행해봤다.

export const options = {
    stages: [
        { duration: '2m', target: 300 },   // 2분 동안 300명의 사용자로 증가
        { duration: '3m', target: 300 },   // 3분 동안 300명 유지
        { duration: '2m', target: 3000 },  // 2분 동안 3000명의 사용자로 증가
        { duration: '3m', target: 3000 },  // 3분 동안 3000명 유지
        { duration: '2m', target: 5000 },  // 2분 동안 5000명의 사용자로 증가
        { duration: '5m', target: 5000 }, // 5분 동안 5000명 유지 (최대 부하)
        { duration: '2m', target: 300 },   // 2분 동안 300명의 사용자로 감소
        { duration: '3m', target: 300 },   // 3분 동안 300명 유지
        { duration: '2m', target: 0 },     // 2분 동안 사용자 수를 0으로 줄임 (테스트 종료)
    ],
};
     checks.........................: 99.99%  ✓ 1556544   ✗ 46     
     data_received..................: 435 MB  274 kB/s
     data_sent......................: 196 MB  124 kB/s
     http_req_blocked...............: avg=21.85µs  min=1µs      med=3µs      max=902.74ms p(90)=6µs    p(95)=19µs   
     http_req_connecting............: avg=12.5µs   min=0s       med=0s       max=679.52ms p(90)=0s     p(95)=0s     
     http_req_duration..............: avg=2.09s    min=429µs    med=402.08ms max=2m33s    p(90)=5.24s  p(95)=5.93s  
       { expected_response:true }...: avg=2.08s    min=429µs    med=402.06ms max=2m32s    p(90)=5.24s  p(95)=5.93s  
     http_req_failed................: 0.00%   ✓ 46        ✗ 1556544
     http_req_receiving.............: avg=5.36ms   min=10µs     med=837µs    max=2m26s    p(90)=6.47ms p(95)=34.91ms
     http_req_sending...............: avg=125.84µs min=4µs      med=15µs     max=2m29s    p(90)=37µs   p(95)=53µs   
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s     p(95)=0s     
     http_req_waiting...............: avg=2.08s    min=383µs    med=400.15ms max=2m33s    p(90)=5.24s  p(95)=5.92s  
     http_reqs......................: 1556590 981.13991/s
     iteration_duration.............: avg=2.04s    min=517.32µs med=1.16s    max=23.41s   p(90)=5.3s   p(95)=5.95s  
     iterations.....................: 1556590 981.13991/s
     vus............................: 2       min=2       max=5000 
     vus_max........................: 5000    min=5000    max=5000

 

평균 요청시간이 2초로 꽤 높다. 최대는 5초까지도 나간다. 서버에 비해 사용자 수가 높다는 생각이 들었다. 이럴 경우 서버 증설이 필요하지 않을까 싶다. 쿼리 외에 들어있는게 없으므로 더 이상의 개선의 여지는 없어 보였다.

 

🤼 전체 시나리오 테스트

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
    scenarios: {
        reservation_process: {
            executor: 'per-vu-iterations',
            vus: 1000, // 동시 사용자의 수
            iterations: 1, // 각 사용자가 한 번씩 시나리오를 수행
            maxDuration: '1m', // 최대 시나리오 수행 시간
        },
    },
};

let globalSeatId = 0; // 전역 변수를 사용하여 전체 시나리오에서 증가

export default function () {
    // 1. 토큰 생성 요청
    let userIdtoken = (__VU - 1) * 10 + __ITER + 1;  // __VU와 __ITER을 조합하여 1부터 시작

    let urltoken = 'http://localhost:8080/api/token';
    let payload1 = JSON.stringify({
        userId: userIdtoken,
    });

    let params1 = {
        headers: {
            'Content-Type': 'application/json',
        },
    };

    let restoken = http.post(urltoken, payload1, params1);
    check(restoken, {
        '토큰 생성 성공': (r) => r.status === 200,
    });

    let tokenData;
    try {
        tokenData = JSON.parse(restoken.body);
    } catch (e) {
        console.error('JSON 파싱 오류:', e.message);
        return;
    }

    let token = tokenData.token;
    let tokenOrder = tokenData.tokenOrder;
    let waitingTime = tokenData.waitingTimeSeconds;

    // Authorization 헤더에 Bearer 토큰 추가
    let authHeader = { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } };

    console.log('토큰 생성 응답: ' + restoken.body);

    // 2. 대기 시간 조건 처리
    if (waitingTime === 0) {
        waitingTime = 10; // waitingTime이 0이면 10초로 설정
    }

    console.log(`Waiting for ${waitingTime} seconds...`);
    sleep(waitingTime); // 지정된 시간 동안 대기

    // 3. 토큰 정보 확인 및 활성화
    let urltoken2 = 'http://localhost:8080/api/token/info';
    let restoken2 = http.get(urltoken2, authHeader);
    check(restoken2, {
        '토큰 활성화 성공': (r) => r.status === 200,
    });

    console.log('토큰 정보 응답: ' + restoken2.body);

    /** 토큰 끝 */

    // 1. 예약 가능 날짜 확인
    let url1 = 'http://localhost:8080/reservation/available/date?concertScheduleId=1';
    let res1 = http.get(url1, authHeader); // 토큰을 포함한 요청
    check(res1, {
        '예약 가능 날짜 확인 성공': (r) => r.status === 200,
    });
    console.log('예약 가능 날짜 확인 응답: ' + res1.body);

    sleep(2); // 사용자가 다음 단계로 넘어가기 전 2초 대기 (thinkingTime)

    // 2. 예약 가능 좌석 확인
    let url2 = 'http://localhost:8080/reservation/available/seat?concertScheduleId=1';
    let res2 = http.get(url2, authHeader); // 토큰을 포함한 요청
    check(res2, {
        '예약 가능 좌석 확인 성공': (r) => r.status === 200,
    });
    console.log('예약 가능 좌석 확인 응답: ' + res2.body);

    sleep(3); // 사용자가 좌석 선택 전 3초 대기 (thinkingTime)

    // 전역 변수로 concertSeatId를 증가시킴
    globalSeatId += 1;

    // 3. 임시 예약
    let concertSeatId = __VU + 1 + __ITER;

    let url3 = 'http://localhost:8080/reservation';
    let payload3 = JSON.stringify({
        concertScheduleId: 1,
        concertId: 1,
        concertSeatId: concertSeatId,
    });

    let res3 = http.post(url3, payload3, authHeader); // 토큰을 포함한 요청
    check(res3, {
        '임시 예약 성공': (r) => r.status === 200,
    });
    console.log('임시 예약 응답: ' + res3.body);

    let concertReservationId = JSON.parse(res3.body).concertReservationId;

    sleep(4); // 사용자가 결제 전 4초 대기 (thinkingTime)

    // 4. 충전
    let userId = (__VU - 1) * 10 + __ITER + 1;  // __VU와 __ITER을 조합하여 1부터 시작

    let url4 = 'http://localhost:8080/wallet/charge';
    let payload4 = JSON.stringify({
        userId: userId,
        amount: 30000,
    });

    let res4 = http.post(url4, payload4, authHeader); // 토큰을 포함한 요청
    check(res4, {
        '충전 성공': (r) => r.status === 200,
    });
    console.log('충전 응답: ' + res4.body);

    sleep(4); // 사용자가 결제 전 4초 대기 (thinkingTime)

    // 5. 결제
    let url5 = 'http://localhost:8080/payment';
    let payload5 = JSON.stringify({
        userId: userId, // 충전에서 사용한 userId
        concertScheduleId: 1,
        concertReservationId: concertReservationId, // 임시 예약에서 얻은 concertReservationId 사용
        concertSeatId: concertSeatId, // 임시 예약에서 사용한 concertSeatId
    });

    let res5 = http.post(url5, payload5, authHeader); // 토큰을 포함한 요청
    check(res5, {
        '결제 성공': (r) => r.status === 200,
    });
    console.log('결제 응답: ' + res5.body);

    sleep(1); // 마지막 단계 후 1초 대기 (thinkingTime)
}

 

처음에는 10명 정도로 시작했다.

   ✓ 토큰 생성 성공
     ✓ 토큰 활성화 성공
     ✓ 예약 가능 날짜 확인 성공
     ✓ 예약 가능 좌석 확인 성공
     ✓ 임시 예약 성공
     ✓ 충전 성공
     ✓ 결제 성공

     checks.........................: 100.00% ✓ 70       ✗ 0   
     data_received..................: 45 kB   1.8 kB/s
     data_sent......................: 16 kB   617 B/s
     http_req_blocked...............: avg=366.17µs min=2µs     med=3.5µs   max=3.41ms   p(90)=2.32ms   p(95)=2.72ms  
     http_req_connecting............: avg=149.98µs min=0s      med=0s      max=1.96ms   p(90)=725.7µs  p(95)=1.13ms  
     http_req_duration..............: avg=149.33ms min=20.23ms med=42.07ms max=692.61ms p(90)=691.59ms p(95)=692.25ms
       { expected_response:true }...: avg=149.33ms min=20.23ms med=42.07ms max=692.61ms p(90)=691.59ms p(95)=692.25ms
     http_req_failed................: 0.00%   ✓ 0        ✗ 70  
     http_req_receiving.............: avg=710.68µs min=19µs    med=224µs   max=6.63ms   p(90)=2.83ms   p(95)=3.06ms  
     http_req_sending...............: avg=87.17µs  min=9µs     med=24.5µs  max=1.05ms   p(90)=267.2µs  p(95)=370.74µs
     http_req_tls_handshaking.......: avg=0s       min=0s      med=0s      max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=148.53ms min=19.92ms med=42.01ms max=689.1ms  p(90)=688.56ms p(95)=688.83ms
     http_reqs......................: 70      2.79053/s
     iteration_duration.............: avg=25.08s   min=25.08s  med=25.08s  max=25.08s   p(90)=25.08s   p(95)=25.08s  
     iterations.....................: 10      0.398647/s
     vus............................: 10      min=10     max=10

 

1. 테스트 개요

  • 동시 사용자 수: 10명
  • 시나리오 반복 횟수: 10회
  • 총 HTTP 요청 수: 70회

2. 평균 요청에 대한 성능 평가

  • 평균 응답 시간: 149.33ms
  • 최소 응답 시간: 20.23ms
  • 최대 응답 시간: 692.61ms
  • 느낌: 최소 응답 시간은 20.23ms로 매우 빠르고, 최대 응답 시간도 692.61ms로 1초를 넘지 않아서 크게 문제될 부분은 없어 보였다.

하지만 1000명으로 유저수를 늘려보았을 때에

 

   ✗ 토큰 생성 성공
      ↳  41% — ✓ 416 / ✗ 584
     ✓ 토큰 활성화 성공
     ✓ 예약 가능 날짜 확인 성공
     ✓ 예약 가능 좌석 확인 성공
     ✓ 임시 예약 성공
     ✓ 충전 성공
     ✓ 결제 성공

 

토큰 생성 성공률이 현저하게 떨어지게 되었다. 원인 파악 및 보수가 필요해보인다. 그렇게 몇번을 바꿔봐도 매번 실패값이 높았다. 내놓은 결론은 실제로 한번에 1000명정도가 서버에 들어오지 않을 뿐더러, 한번에 많은 부하를 서버가 받아들이지 못하는 것 같았다. (실제로 back단에는 log가 찍히지 않는다.)

 

방법을 다르게해서 부하를 서서히 주는.. 그래프와 같은 방식으로 넣어봤다.

 

  • LOAD TEST
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
    scenarios: {
        reservation_process: {
            executor: 'ramping-vus', // 사용자 수를 점진적으로 증가시키는 executor
            startVUs: 0, // 시작 시 동시 사용자 수
            stages: [
                { duration: '30s', target: 100 }, // 30초 동안 동시 사용자 수를 100으로 증가
                { duration: '1m', target: 1000 }, // 1분 동안 동시 사용자 수를 1000으로 증가
                { duration: '30s', target: 0 },   // 30초 동안 동시 사용자 수를 0으로 감소
            ],
        },
    },
};

let globalSeatId = 0; // 전역 변수를 사용하여 전체 시나리오에서 증가

export default function () {
    // 1. 토큰 생성 요청
    let userIdtoken = (__VU - 1) * 10 + __ITER + 1;  // __VU와 __ITER을 조합하여 1부터 시작

    let urltoken = 'http://localhost:8080/api/token';
    let payload1 = JSON.stringify({
        userId: userIdtoken,
    });

    let params1 = {
        headers: {
            'Content-Type': 'application/json',
        },
    };

    let restoken = http.post(urltoken, payload1, params1);
    check(restoken, {
        '토큰 생성 성공': (r) => r.status === 200,
    });

    let tokenData;
    try {
        tokenData = JSON.parse(restoken.body);
    } catch (e) {
        console.error('JSON 파싱 오류:', e.message);
        return;
    }

    let token = tokenData.token;
    let tokenOrder = tokenData.tokenOrder;
    let waitingTime = tokenData.waitingTimeSeconds;

    // Authorization 헤더에 Bearer 토큰 추가
    let authHeader = { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } };

    console.log('토큰 생성 응답: ' + restoken.body);

    // 2. 대기 시간 조건 처리
    if (waitingTime === 0) {
        waitingTime = 10; // waitingTime이 0이면 10초로 설정
    }

    console.log(`Waiting for ${waitingTime} seconds...`);
    sleep(waitingTime); // 지정된 시간 동안 대기

    // 3. 토큰 정보 확인 및 활성화
    let urltoken2 = 'http://localhost:8080/api/token/info';
    let restoken2 = http.get(urltoken2, authHeader);
    check(restoken2, {
        '토큰 활성화 성공': (r) => r.status === 200,
    });

    console.log('토큰 정보 응답: ' + restoken2.body);


    // 1. 예약 가능 날짜 확인
    let url1 = 'http://localhost:8080/reservation/available/date?concertScheduleId=1';
    let res1 = http.get(url1, authHeader); // 토큰을 포함한 요청
    check(res1, {
        '예약 가능 날짜 확인 성공': (r) => r.status === 200,
    });
    console.log('예약 가능 날짜 확인 응답: ' + res1.body);

    sleep(2); // 사용자가 다음 단계로 넘어가기 전 2초 대기 (thinkingTime)

    // 2. 예약 가능 좌석 확인
    let url2 = 'http://localhost:8080/reservation/available/seat?concertScheduleId=1';
    let res2 = http.get(url2, authHeader); // 토큰을 포함한 요청
    check(res2, {
        '예약 가능 좌석 확인 성공': (r) => r.status === 200,
    });
    console.log('예약 가능 좌석 확인 응답: ' + res2.body);

    sleep(3); // 사용자가 좌석 선택 전 3초 대기 (thinkingTime)

    // 전역 변수로 concertSeatId를 증가시킴
    globalSeatId += 1;

    // 3. 임시 예약
    let concertSeatId = __VU + 1 + __ITER;

    let url3 = 'http://localhost:8080/reservation';
    let payload3 = JSON.stringify({
        concertScheduleId: 1,
        concertId: 1,
        concertSeatId: concertSeatId,
    });

    let res3 = http.post(url3, payload3, authHeader); // 토큰을 포함한 요청
    check(res3, {
        '임시 예약 성공': (r) => r.status === 200,
    });
    console.log('임시 예약 응답: ' + res3.body);

    let concertReservationId = JSON.parse(res3.body).concertReservationId;

    sleep(4); // 사용자가 결제 전 4초 대기 (thinkingTime)

    // 4. 충전
    let userId = (__VU - 1) * 10 + __ITER + 1;  // __VU와 __ITER을 조합하여 1부터 시작

    let url4 = 'http://localhost:8080/wallet/charge';
    let payload4 = JSON.stringify({
        userId: userId,
        amount: 30000,
    });

    let res4 = http.post(url4, payload4, authHeader); // 토큰을 포함한 요청
    check(res4, {
        '충전 성공': (r) => r.status === 200,
    });
    console.log('충전 응답: ' + res4.body);

    sleep(4); // 사용자가 결제 전 4초 대기 (thinkingTime)

    // 5. 결제
    let url5 = 'http://localhost:8080/payment';
    let payload5 = JSON.stringify({
        userId: userId, // 충전에서 사용한 userId
        concertScheduleId: 1,
        concertReservationId: concertReservationId, // 임시 예약에서 얻은 concertReservationId 사용
        concertSeatId: concertSeatId, // 임시 예약에서 사용한 concertSeatId
    });

    let res5 = http.post(url5, payload5, authHeader); // 토큰을 포함한 요청
    check(res5, {
        '결제 성공': (r) => r.status === 200,
    });
    console.log('결제 응답: ' + res5.body);

    sleep(1); // 마지막 단계 후 1초 대기 (thinkingTime)
}
 ✓ 토큰 생성 성공
     ✓ 토큰 활성화 성공
     ✓ 예약 가능 날짜 확인 성공
     ✓ 예약 가능 좌석 확인 성공
     ✗ 임시 예약 성공
      ↳  90% — ✓ 1520 / ✗ 167
     ✓ 충전 성공
     ✗ 결제 성공
      ↳  90% — ✓ 1404 / ✗ 148

     checks.........................: 97.33% ✓ 11498     ✗ 315   
     data_received..................: 145 MB 995 kB/s
     data_sent......................: 2.6 MB 18 kB/s
     http_req_blocked...............: avg=102.57µs min=1µs    med=5µs    max=56.71ms  p(90)=343.8µs p(95)=493µs   
     http_req_connecting............: avg=72.71µs  min=0s     med=0s     max=55.93ms  p(90)=263µs   p(95)=380.39µs
     http_req_duration..............: avg=2.07s    min=1.74ms med=1.6s   max=10.47s   p(90)=4.7s    p(95)=6.24s   
       { expected_response:true }...: avg=2.08s    min=1.74ms med=1.61s  max=10.47s   p(90)=4.69s   p(95)=6.23s   
     http_req_failed................: 2.66%  ✓ 315       ✗ 11498 
     http_req_receiving.............: avg=14.72ms  min=22µs   med=1.4ms  max=1.35s    p(90)=38.43ms p(95)=48.34ms 
     http_req_sending...............: avg=71.26µs  min=8µs    med=28µs   max=189.23ms p(90)=75µs    p(95)=109µs   
     http_req_tls_handshaking.......: avg=0s       min=0s     med=0s     max=0s       p(90)=0s      p(95)=0s      
     http_req_waiting...............: avg=2.06s    min=1.63ms med=1.59s  max=10.47s   p(90)=4.68s   p(95)=6.23s   
     http_reqs......................: 11813  81.193602/s
     iteration_duration.............: avg=40.63s   min=24.1s  med=42.81s max=1m0s     p(90)=51.82s  p(95)=53.74s  
     iterations.....................: 1521   10.4542/s
     vus............................: 1      min=1       max=1000
     vus_max........................: 1000   min=1000    max=1000

 

- 실패가 10퍼센트

- 응답,요청 시간 등이 매우 많이 나간다.

테스트시에 실패는 많이 줄은 모습이지만, 역시나 서버의 성능을 따라가지 못한다. 실무에서는 서버를 늘리는 등의 방법도 있겠지만, 현재는 코드에서 변경이 가능하다면 수정이 필요하다.

 

 

  • 임시 예약 및 결제 단계: 두 단계에서 각각 10%의 실패율이 발생, 이 단계들은 추가적인 최적화가 필요합
  • 응답 시간: 응답 시간이 특히 높은 상황에서 서버의 성능 최적화가 필요, 최대 응답 시간을 줄이고, P90 및 P95 응답 시간을 개선할 필요가 있다.
  • 서버 자원 관리: 이 부하에서 서버 자원이 충분히 사용되고 있는지, 과도한 부하로 인해 어떤 자원이 부족한지 (예: CPU, 메모리, 디스크 I/O 등) 분석이 필요하다.

 

 

'향해99' 카테고리의 다른 글

k6 간단 공부  (0) 2024.08.20
kafka 설치  (0) 2024.08.14
kafka에 대해 공부해보자  (0) 2024.08.12
9주차 회고노트  (0) 2024.08.10
9주차 발제  (0) 2024.08.10
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함