6. Jmeter 를 활용한 Kafka 병목 현상 테스트
6.1. prodcer/consumer 수정 및 주문 API 서비스 로직 수정
6.1.1. OrderProducer/OrderConsumer 수정
굳이 객체 전체를 넘기지 않아도 되므로 메세지 value 값도 string(unique 한 주문번호) 으로 수정
(필요에 따라 application.yml 의 기본 producer/consumer 설정 변경하거나, String 타입의 별도 kafka factory 설정 추가)
- OrderProducer
private final KafkaTemplate<String, String> kafkaTemplate;
public void sendOrder(String orderNumber) {
kafkaTemplate.send(TOPIC, orderNumber);
}
- OrderConsumer
private static final String TOPIC = "orders";
private final KafkaTemplate<String, String> kafkaTemplate;
public void sendOrder(String orderNumber) {
kafkaTemplate.send(TOPIC, orderNumber);
}
6.1.2. OrderService 수정
Kafka 메세지 전송 전에 PROCESSING(진행중) 상태로 주문데이터 저장 로직 추가
-> 상태 전이 관리하여 중복 요청 방지를 위해
디버깅 로그 추가
package com.toy.service;
import com.toy.dto.ApiResponseDto;
import com.toy.dto.OrderRequestDto;
import com.toy.kafka.OrderProducer;
import com.toy.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final OrderProducer orderProducer;
public ApiResponseDto order(OrderRequestDto orderRequestDto) {
log.debug("Order Request !! [" + orderRequestDto.getOrderNumber() + "]");
orderRepository.save(orderRequestDto.toEntity());
orderProducer.sendOrder(orderRequestDto.getOrderNumber());
return ApiResponseDto.builder().result(true).message("주문 등록 요청 성공").build();
}
}
6.2. 주문 저장 로직에 병목 현상 구현
6.2.1. repository 에 조건부 상태 변경 추가
지정 한 상태일 때만 상태가 변경 될 수 있도록 JPQL 추가
package com.toy.repository;
import com.toy.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
@Modifying
@Query("UPDATE Order o SET o.status = :newStatus WHERE o.orderNumber = :orderNumber AND o.status = :expectedStatus")
int updateStatusConditionally(@Param("orderNumber") String orderNumber,
@Param("expectedStatus") Order.OrderStatus expectedStatus,
@Param("newStatus") Order.OrderStatus newStatus);
}
6.2.2. 주문 저장 로직 변경
병목 현상 구현을 위해 2초 sleep 추가
주문 상태가 PROCESSING(진행중) 이면 DONE(완료) 처리하여 저장
-> 주문 상태 완료 update 결과가 0인 경우 이미 처리 된 주문으로 가정하여 debug log 찍어두기
package com.toy.service;
import com.toy.entity.Order;
import com.toy.repository.OrderRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderStorageService {
private final OrderRepository orderRepository;
@Transactional
public void store(String orderNumber) {
log.debug("Order Store Start !! [" + orderNumber + "]");
try {
// 비즈니스 처리 소요 시간 가정 2초
Thread.sleep(2000);
int result = orderRepository.updateStatusConditionally(orderNumber, Order.OrderStatus.PROCESSING, Order.OrderStatus.DONE);
if(result == 0) {
log.debug("Already Stored Order :( [" + orderNumber + "]");
return;
}
} catch (Exception e) {
orderRepository.updateStatusConditionally(orderNumber, Order.OrderStatus.PROCESSING, Order.OrderStatus.FAILED);
}
log.debug("Order Store End !! [" + orderNumber + "]");
}
}
6.3. Jmeter 파일 다운로드
Jmeter 공식 다운로드 페이지 접속하여 압축파일 다운로드
https://jmeter.apache.org/download_jmeter.cgi
6.4. Jmeter 실행
압축 해제 한 Jmeter 폴더\bin\jmeter.bat 클릭 하여 Jmeter GUI 실행
6.5. Jmeter 설정
6.5.1. Thread Group 추가
Test Plan 우클릭 > Add > Threads (Users) > Thread Group
1초 동안 사용자 10명 동시 요청 환경 설정
- Number of Threads : Thread 수 지정 (동시 요청 사용자 수)
- Ramp-up period : 요청 텀 지정
- Loop Count : 반복 횟수 지정
6.5.2. Http Request 추가
Thread Group 우클릭 > Add > Sampler > Http Request
API Url, method 설정
Jmeter 함수 활용하여 Request Body 설정
- __time(yyyyMMddHHmmss) : 현재 시간 문자열 변환
- __threadNum : 스레드 번호 (각각 다른 번호 주기 위해 활용)
{
"orderNumber": "order_${__time(yyyyMMddHHmmss)}_${__threadNum}",
"productId": 1,
"userId": "haley${__threadNum}",
"quantity": 1
}
6.5.3. Http Header Manager 추가
Http Request 우클릭 > Add > Config Element > Http Header Manager
Content-Type : application/json 추가
6.5.4. View Results Tree 추가
Thread Group 우클릭 > Add > Listener > View Results Tree
6.6. Jmeter 테스트 실행
상단 실행 버튼 클릭하고 View Results Tree 클릭하여
Request 정상 호출 되었는지 확인
6.7. 병목 현상 확인
OrderService, OrderStorageService 에 찍어둔 debug log 확인
Order API 호출은 0.4초에 10회 모두 요청했지만,
실제 주문 저장 처리는 12분 00초 부터 12분 20초 까지 약 20초 소요
-> consumer concurrency 기본값은 1 이므로, sleep 2초 * 10회 하여 총 20초
17:11:57.969+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_6]
17:11:57.969+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_2]
17:11:57.969+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_3]
17:11:57.970+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_4]
17:11:57.969+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_7]
17:11:57.969+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_5]
17:11:57.973+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_1]
17:11:58.002+09:00 DEBUG 19192 : Order Request !! [order_20250509171157_8]
17:11:58.185+09:00 DEBUG 19192 : Order Request !! [order_20250509171158_9]
17:11:58.380+09:00 DEBUG 19192 : Order Request !! [order_20250509171158_10]
17:12:00.232+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_4]
17:12:02.465+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_4]
17:12:02.486+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171158_10]
17:12:04.496+09:00 DEBUG 19192 : Order Store End !! [order_20250509171158_10]
17:12:04.519+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_8]
17:12:06.543+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_8]
17:12:06.605+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_7]
17:12:08.627+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_7]
17:12:08.643+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171158_9]
17:12:10.667+09:00 DEBUG 19192 : Order Store End !! [order_20250509171158_9]
17:12:10.699+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_6]
17:12:12.708+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_6]
17:12:12.733+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_2]
17:12:14.756+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_2]
17:12:14.789+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_1]
17:12:16.811+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_1]
17:12:16.840+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_3]
17:12:18.854+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_3]
17:12:18.887+09:00 DEBUG 19192 : Order Store Start !! [order_20250509171157_5]
17:12:20.898+09:00 DEBUG 19192 : Order Store End !! [order_20250509171157_5]
'Spring > Toy' 카테고리의 다른 글
5. Kafka > Consumer 생성 및 메세지 처리 (0) | 2025.04.29 |
---|---|
4. Kafka > API + Producer 생성 (0) | 2025.04.29 |
3. 프로젝트 생성 및 설정 > SpringBoot3 + Java17 + Kafka + gradle + Mysql (1) | 2025.04.29 |
2. 환경구성 > Docker DB(Mysql) + Kafka + Zookeeper 띄우기 (1) | 2025.04.29 |
Docker Desktop 설치 후 실행 안되는 경우 (1) | 2025.04.29 |