ν μ€νΈ λμ:
ν μ€νΈ λͺ©μ :
μλλ¦¬μ€ μΈλΆ λμ:
1. ν ν° μμ± API νΈμΆ - μ¬μ©μκ° μ½μνΈ μμ½μ μμνκΈ° μ μ ν ν°μ μμ±ν©λλ€.
2. λκΈ°μ΄ μ 보 API νΈμΆ - λκΈ°μ΄μ λν μ 보λ₯Ό μ‘°νν©λλ€.
3. μ½μνΈ μΌμ μ‘°ν API νΈμΆ - μ¬μ©μκ° μνλ μ½μνΈμ μΌμ μ νμΈν©λλ€.
4. μ½μνΈ μ’μ μ‘°ν API νΈμΆ - μ¬μ©μκ° μνλ μ½μνΈμ μ’μ μ 보λ₯Ό μ‘°νν©λλ€.
5. μ½μνΈ μ’μ μμ½ API νΈμΆ - μ¬μ©μκ° μνλ μ½μνΈμ μ’μμ μμ½ν©λλ€.
import http from 'k6/http';
import { check, sleep } from 'k6';
export let options = {
vus: 100, // λμ μμ² μ
duration: '20s', // ν
μ€νΈ μ§μ μκ°
};
export default function () {
const userId = Math.floor(Math.random() * 100000) + 1;
const tokenUrl = `http://localhost:8082/api/queue/tokens/users/${userId}`;
// 1. λκΈ°μ΄ ν ν° μμ± μμ²
const tokenHeaders = {
'Content-Type': 'application/json',
};
const tokenResponse = http.post(tokenUrl, JSON.stringify({}), { headers: tokenHeaders });
check(tokenResponse, {
'Token creation is status 200': (r) => r.status === 200,
});
if (tokenResponse.status !== 200) {
console.error(`Failed to create token for userId: ${userId}, Status: ${tokenResponse.status}`);
sleep(1);
return;
}
const tokenData = tokenResponse.json();
const queueToken = tokenData.queueToken; // μμ±λ λκΈ°μ΄ ν ν°
// 2. λκΈ°μ΄ μ 보 ν΄λ§ μ‘°ν
const queueInfoUrl = 'http://localhost:8082/api/queue/tokens/users';
const queueInfoHeaders = {
'Content-Type': 'application/json',
'QUEUE-TOKEN': queueToken, // λκΈ°μ΄ ν ν° μ€μ
};
let queuePosition = -1;
let queueInfoResponse;
// λκΈ°μ΄ μνκ° -1μ΄ μλ κ²½μ° κ³μ ν΄λ§νμ¬ μν νμΈ
while (queuePosition !== -1) {
queueInfoResponse = http.get(queueInfoUrl, { headers: queueInfoHeaders });
check(queueInfoResponse, {
'Queue info is status 200': (r) => r.status === 200,
});
if (queueInfoResponse.status !== 200) {
console.error(`Failed to fetch queue info with token: ${queueToken}, Status: ${queueInfoResponse.status}`);
sleep(1);
return;
}
const queueInfo = queueInfoResponse.json();
queuePosition = queueInfo.queuePosition;
if (queuePosition !== -1) {
console.log(`Waiting for position to be -1, current position: ${queuePosition}`);
sleep(3); // μΌμ μκ° ν λ€μ λκΈ°μ΄ μνλ₯Ό νμΈ
}
}
// 3. μ½μνΈ μ€μΌμ€ μ‘°ν
const concertScheduleUrl = 'http://localhost:8082/api/concerts/1/schedules';
const concertScheduleHeaders = {
'Content-Type': 'application/json',
'QUEUE-TOKEN': queueToken, // λκΈ°μ΄ ν ν° μ€μ
};
const concertScheduleResponse = http.get(concertScheduleUrl, { headers: concertScheduleHeaders });
check(concertScheduleResponse, {
'Concert schedule is status 200': (r) => r.status === 200,
});
if (concertScheduleResponse.status !== 200) {
console.error(`Failed to fetch concert schedule with token: ${queueToken}, Status: ${concertScheduleResponse.status}`);
sleep(1);
return;
}
// 4. μ½μνΈ μ’μ μ 보 μ‘°ν
const concertSeatsUrl = 'http://localhost:8082/api/concerts/1/schedules/1/seats';
const concertSeatsHeaders = {
'Content-Type': 'application/json',
'QUEUE-TOKEN': queueToken, // λκΈ°μ΄ ν ν° μ€μ
};
const concertSeatsResponse = http.get(concertSeatsUrl, { headers: concertSeatsHeaders });
check(concertSeatsResponse, {
'Concert seats is status 200': (r) => r.status === 200,
});
if (concertSeatsResponse.status !== 200) {
console.error(`Failed to fetch concert seats with token: ${queueToken}, Status: ${concertSeatsResponse.status}`);
sleep(1);
return;
}
// 5. μ’μ μμ½ μμ²
const reservationUrl = 'http://localhost:8082/api/reservations';
const reservationHeaders = {
'Content-Type': 'application/json',
'QUEUE-TOKEN': queueToken,
};
const seatNumber = Math.floor(Math.random() * (172 - 120 + 1)) + 120;
const seatIdsArr = [seatNumber];
const reservationPayload = JSON.stringify({
concertId: 1,
scheduleId: 1,
seatIdsArr: seatIdsArr, // λ°°μ΄λ‘ μ€μ
userId: userId
});
const reservationResponse = http.post(reservationUrl, reservationPayload, { headers: reservationHeaders });
check(reservationResponse, {
'Reservation is status 200': (r) => r.status === 200,
});
if (reservationResponse.status === 200) {
console.log(`Reservation succeeded for userId: ${userId}, Seats: ${seatIdsArr}`);
} else {
console.error(`Reservation failed for userId: ${userId}, Status: ${reservationResponse.status}`);
console.error(`Reservation failed. Response: ${reservationResponse.body}`);
}
sleep(1); // μμ² κ° κ°κ²©
}
μ§ν | κ° |
---|---|
μ±κ³΅μ μΈ μμ² λΉμ¨ | 50.94% (1028/2018) |
μ±κ³΅μ μΈ μμ² (200 OK) | 90% (9/10) |
HTTP μμ² μ€ν¨ λΉμ¨ | 49.05% (990/2018) |
νκ· μλ΅ μκ° | 518.26ms |
μ΅λ μλ΅ μκ° | 6.3s |
μμ² μ (μ 체) | 2018 |
μμ² μ±κ³΅ λΉμ¨ | 50.94% |
λμ μ¬μ©μ (VUs) | νκ· 31λͺ , μ΅λ 100λͺ |
ν μ€νΈ μ§ν μκ° | νκ· 2.06μ΄ |
HikariPool-1 - Connection is not available
μ€λ₯κ° λ°μν μ΄μ λ λ°μ΄ν°λ² μ΄μ€ μ°κ²° ν ν¬κΈ°κ° λ무 μκ±°λ, λμ μ μ μ¬μ©μκ° λ§μμ§λ©΄μ 컀λ₯μ
νμ΄ κ³ κ°λμκΈ° λλ¬Έμ
λλ€.λμμ± μ μ΄
λ° μ€μΌμ€λ§
μκ³ λ¦¬μ¦μ κ°μ νμ¬ μμ²μ λΉ λ₯΄κ² μ²λ¦¬ν μ μλλ‘ κ°μ ν©λλ€.JDBCConnectionException
μ΄ λ°μνμμΌλ―λ‘, 컀λ₯μ
ν ν¬κΈ°λ₯Ό λλ¦¬κ³ νμμμ μ€μ μ μ‘°μ ν©λλ€.HikariCP
μ€μ μ ν΅ν΄ 컀λ₯μ
νμ ν¬κΈ°λ₯Ό λλ¦¬κ³ , maxLifetime
, idleTimeout
λ±μ μ‘°μ ν©λλ€.