본문 바로가기
관리자

Programming-[Backend]/Performance, Monitoring

성능 테스트: jmeter, @Profile, chaos monkey

728x90
반응형

 

 

배경

로컬에서 잘 만든 애플리케이션을 클라우드 환경 등에 배포하기 위해서는 컴퓨팅 자원을 얼마나 쓸 것인지 결정하는 것이 필요하다. 실제 애플리케이션의 요구사항에 따른 적절한 자원 임계치를 설정하고 그에 맞게 설정해야 불필요한 자원 낭비를 막을 수 있다.
 

jmeter

애플리케이션을 테스트하기 위한 대상 API를 선정했다면, 해당 API 쪽으로 많은 요청을 한 번에 쏴볼 수 있는 도구가 필요한데, 이를 가능하게 해주는 것이 jmeter이다.
 
아래 jmeter 홈페이지에서 실행파일이 포함된 바이너리 파일을 다운로드 받고, 압축을 푼 뒤, 내부의 /bin/jmeter 파일을 실행하면된다.
https://jmeter.apache.org/download_jmeter.cgi
 
아래 그림처럼 Thread Group -> Http Sampler 등을 만들고 테스트하면 된다. 각종 그래프용 플러그인이나 Thread Group용 Ultimate Thread Group 플러그인 등을 사용해야 부하 테스트를 좀 더 정확히 할 수 있다. 플러그인이나 테스트 방법 등은 다른 블로그들의 글을 참조로 넣어둔다.

 
https://leeggmin.tistory.com/10
https://m.blog.naver.com/sinx2233/223235159196
https://sooo-9.tistory.com/37
https://blog.naver.com/ka28/222492575671
 
 
조건을 조절해가면서 아래 표와 같이 테스트했었다.

 
그리고 Jvm Heap memory를 추적하고, Heap Dump 내용도 살펴보았다. JVM Heap에서 톱니바퀴 모양의 패턴이 나오는것이 정상이다. 그리고 Memory가 수직으로 떨어지는 것은 GC가 일어나기 때문이다. GC후 메모리가 감소하거나 동일한 수치로 계속해서 떨어지면 문제가 없지만 만약 GC후 떨어지는 메모리의 값이 시간이 지날수록 계속해서 상승한다면 Memory Leak이 발생하는 것이므로 유의해야한다.

 
 
 

@Profile

기존 코드를 그대로 사용할 수 있는 경우도 있겠으나, 외부 서비스와의 통신 등으로 기존 코드를 그대로 사용하지 못하는 경우 Bean을 따로 구현해줘야한다.
 
예를 들어 아래와 같이 HttpRequestService를 구현했다면, benchmark 환경을 구성하고 해당 Bean의 구현체를 하나 더 작성해준다.
 

//어노테이션 생략
//@Service는 제거
public class HttpRequestServiceImpl implements HttpRequestService {

private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;

@Override
public <R extends BaseRoAbstract, T extends BaseDtoAbstract> ResponseEntity<R> sendJsonRequest(
	String url,
	HttpHeaders headers,
	HttpMethod httpMethod,
	T requestBodyDto,
	Class<R> responseType) {
	String body;
	try {
	body = objectMapper.writeValueAsString(requestBodyDto);
	} catch (JsonProcessingException e) {
	log.error("Serialization error: {}", e.getMessage());
	throw new RuntimeException("Failed to serialize request body", e);
	}

	HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);

	try {
	return restTemplate.exchange(new URI(url), httpMethod, requestEntity, responseType);
	} catch (HttpMessageNotReadableException e) {
    
    //...중략

 
benchmark용 구현체

//어노테이션 생략
//@Service는 제거
public class HttpRequestServiceBenchmarkImpl implements HttpRequestService {

@Override
public <R extends BaseRoAbstract, T extends BaseDtoAbstract> ResponseEntity<R> sendJsonRequest(
	String url,
	HttpHeaders headers,
	HttpMethod httpMethod,
	T requestBodyDto,
	Class<R> responseType) {
	log.info("[Benchmark] sendJsonRequest Called");
	return ResponseEntity.ok(null);
}

 
그리고 아래에서 @Profile을 적용하여 환경 변수에 따라 특정 빈만 등록되도록 해준다.
여기서 주의할점은, 아래처럼 Config를 적용할려면 두 구현체 클래스의 위에 @Service 어노테이션을 제거해줘야한다는 것이다. 그렇지 않으면 같은 이름의 Bean이 중복되어 올라가서 에러가 난다. 또한 메서드의 이름(defaulHttpRequestService, testHttpRequestService)와 빈 클래스 및 이름(HttpRequestService, httpRequestService)가 일치하지 않으므로 사용에 유의해야한다.

@Configuration
public class HttpRequestServiceConfig {

@Bean
@Profile("!benchmark")
public HttpRequestService defaultHttpRequestService(
	RestTemplate restTemplate, ObjectMapper objectMapper) {
	return new HttpRequestServiceImpl(restTemplate, objectMapper);
}

@Bean
@Profile("benchmark")
public HttpRequestService testHttpRequestService() {
	// Test 환경에서 사용할 Custom 빈
	return new HttpRequestServiceBenchmarkImpl();
}

 
 
그리고 benchmark 환경을 구성하여 해당 profile로 실행하고, jmeter를 통해 요청을 보내면 된다.

spring:
  application:
    name: ...
  profiles:
    active: benchmark

 
 

Chaos Monkey

실제 API 요청에서 생기는 부하가 2~2.5초 정도가 걸리는 경우, 위에서 작성한 benchmark 서비스로 부하를 재현해야한다. 이 때 사용할 수 있는 것이 Choas Monkey이다. Chaos Monkey는 내 서버에 지연, 예외 발생 공격을 해주고 그 빈도도 조절할 수 있다. 아래처럼 설정하면 된다. 자세한 설정들은 공식문서에서 찾아봐야한다. GPT는 아직까지는 정확히 못 알려주는 것 같다.

chaos:
  monkey:
    enabled: true
    watcher:
      bean-classes:
        - com.example.project.//...//.benchmark....BenchmarkImpl
    assaults:
      latency-active: true # 지연 활성화
      latency-range-start: 2000 # 최소 지연 시간 (ms)
      latency-range-end: 2500 # 최대 지연 시간 (ms)
      exceptions-active: false # 예외 공격 활성 여부
      exception:
        type: org.springframework.web.client.ResourceAccessException
      level: 1 # 1: 요청마다 공격 ~ 10000: 10000개 중 10000번째 요청만 공격

management:
  endpoint:
    chaosmonkey:
      enabled: true
  endpoints:
    web:
      exposure:
        include:
          - health
          - info
          - chaosmonkey

 
bean-clasess 항목을 통해 특정 빈에만 공격을 활성화할 수 있다. 나머지 내용들은 주석으로 표기한 내용대로이다.
 
management 부분은 spring actuator가 설치됐을 때 사용하는 부분으로, chaos monkey가 제공하는 특정 api로 REST API 요청을 보내면 위에서 설정한 지연시간, 요청 level 등을 API로 조절할 수 있게 된다.
 
 
그리고, Chaos Monkey가 정상적으로 동작하게 할려면 profile명이 chaos-monkey 여야한다. 커스텀한 profile 명으로 실행하고 싶다면 아래 옵션을 JVM 실행 옵션에 추가해줘야한다.

-DLOAD_CHAOS_MONKEY=true

 
 
정상적으로 실행하면 콘솔에 아래처럼 뜬다.

 
공격시 로그는 아래처럼 뜬다.

 

728x90
반응형