1. 대표적인 장애 상황
- CPU 사용량이 과도하게 많아질 때
- JVM 메모리가 과도하게 많아질 때
- 데이터베이스 연결(Database Connection)이 과도하게 많아질 때
- 에러 로그가 갑자기 치솟을 때
2. CPU 사용량 시뮬레이션
package com.monitor.sample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@RestController
public class TrafficController {
/**
* cpu 사용량을 늘리기위해 반복문을 돌려준다.
* */
@GetMapping("cpu")
public String cpu() {
log.info("cpu");
long value = 0;
for (long i = 0; i < 100_000_000_000L; i++) {
value++;
}
return "ok value=" + value;
}
}
반복문을 돌면서 CPU를 괴롭혀보자
2 - 1. 모니터링 화면
system cpu와 process cpu가 급격하게 치솟고 있다.
2 - 2. CPU 사용량이 치솟을 때 개발자가 취할 수 있는 주요 조치
2 - 2 - 1. 프로파일링 도구 활용
Visual Studio Profile 이나 JetBrains dotTrace 같은 도구를 사용하여 CPU 사용량이 높은 코드 영역을 정확히 파악하고,
해당 코드 영역을 리팩토링한다.
2 - 2 - 2. 코드 최적화
비효율적이거나 불필요하게 복잡한 알고리즘을 최대한 단순화하고
과도한 반복문과 재귀 호출을 최소화한다.
2 - 2 - 2. 데이터베이스 최적화
적절한 인덱스, 과도한 조인 제거, 필요한 데이터만 조회하는 방식으로
쿼리 성능을 개선하면 cpu 사용량을 줄일 수 있다.
2 - 2 - 3. 캐싱 구현
자주 접근하는 데이터에 대한 인메모리 캐싱을 구현하여
동일한 API 요청에 대해 반복적인 처리를 방지한다면 cpu 사용량을 줄일 수 있다.
3. JVM 메모리 장애 상황 시뮬레이션
package com.monitor.sample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@RestController
public class TrafficController {
/**
* cpu 사용량을 늘리기위해 반복문을 돌려준다.
* */
@GetMapping("cpu")
public String cpu() {
log.info("cpu");
long value = 0;
for (long i = 0; i < 100_000_000_000L; i++) {
value++;
}
return "ok value=" + value;
}
private List<String> list = new ArrayList<>();
/**
* jvm
* */
@GetMapping("/jvm")
public String jvm() {
log.info("jvm");
for (int i = 0; i < 1_000_000; i++) {
list.add("hello jvm!" + i);
}
return "ok";
}
}
list에 문자열을 반복해서 저장함으로써, heap 메모리를 낭비하도록 해보자
3 - 1. 모니터링 화면
JVM 메모리가 증가하고 있음을 모니터링 화면을 통해 확인할 수 있고,
만약, 최대치를 넘게되면 OutOfMemory 에러를 만나게 된다.
Heap 메모리 뿐만 아니라
GC 발생 빈도와 소요시간, 메타스페이스 사용량도 같이 체크해줘야 확실한 JVM 메모리 모니터링을 할 수 있다.
3 - 2. JVM 메모리 장애상황 시 개발자가 취할 수 있는 조치
- 프로파일링 도구(VisualVM, JMC)와 같은 도구를 활용
- 힙 덤프를 분석하여 메모리 누수 지점 파악
- 적절한 GC 알고리즘을 선택
- 불필요한 객체 생성을 최소화
4. Database Connection 장애 상황 시뮬레이션
package com.monitor.sample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@RestController
public class TrafficController {
/**
* cpu 사용량을 늘리기위해 반복문을 돌려준다.
* */
@GetMapping("cpu")
public String cpu() {
log.info("cpu");
long value = 0;
for (long i = 0; i < 100_000_000_000L; i++) {
value++;
}
return "ok value=" + value;
}
private List<String> list = new ArrayList<>();
/**
* jvm
* */
@GetMapping("/jvm")
public String jvm() {
log.info("jvm");
for (int i = 0; i < 1_000_000; i++) {
list.add("hello jvm!" + i);
}
return "ok";
}
/**
* jdbc connection
* */
@Autowired
DataSource dataSource;
@GetMapping("/jdbc")
public String jdbc() throws SQLException {
log.info("jdbc");
Connection conn = dataSource.getConnection();
// conn.close(); // 커넥션을 닫지 않는다.
return "ok";
}
}
커넥션을 가져온다음에 커넥션을 닫지 않고 메소드가 종료되도록 하고 있다.
이 경우 커넥션을 커넥션 풀에 반납하지 않게 된다.
4 - 1. 모니터링 화면
커넥션 풀 사이즈가 10개인 상황에서
10번만 호출하면 모든 커넥션을 사용하는 상황이 발생한다.
더 호출하게 되면 Pending 상태의 커넥션이 생기게 된다.
그리고, Pending 상태에서 타임아웃으로 인해 종료되는 커넥션도 생기게 된다.
그리고, 아래와 같은 에러를 만나게 된다.
에러 메세지를 보면 총 10개 중 10개가 활성화 상태이고, 30초가 지난 후 커넥션이 사용불가라는 에러가 발생했다.
4 - 2. DB Connection 장애 상황시 개발자가 취할 수 있는 조치
보통, DB Connection 에서 장애 상황이 난다면, 쿼리가 느리다는 의미가 될 것이다.
장기적으로, 쿼리 성능 체크 및 쿼리 튜닝을 해야하고,
커넥션 풀 설정도 최적화해야 한다.
급박한 상황이라면 서버나 데이터베이스를 늘려야 한다.
5. Error log 가 급격하게 쌓이는 상황 시뮬레이션
package com.monitor.sample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@RestController
public class TrafficController {
/**
* cpu 사용량을 늘리기위해 반복문을 돌려준다.
* */
@GetMapping("cpu")
public String cpu() {
log.info("cpu");
long value = 0;
for (long i = 0; i < 100_000_000_000L; i++) {
value++;
}
return "ok value=" + value;
}
private List<String> list = new ArrayList<>();
/**
* jvm
* */
@GetMapping("/jvm")
public String jvm() {
log.info("jvm");
for (int i = 0; i < 1_000_000; i++) {
list.add("hello jvm!" + i);
}
return "ok";
}
/**
* jdbc connection
* */
@Autowired
DataSource dataSource;
@GetMapping("/jdbc")
public String jdbc() throws SQLException {
log.info("jdbc");
Connection conn = dataSource.getConnection();
// conn.close(); // 커넥션을 닫지 않는다.
return "ok";
}
/**
* error log
* */
@GetMapping("/error-log")
public String error() {
log.error("error log");
return "error";
}
}
5 - 1. 모니터링 화면
에러 로그가 쌓이게 되면, 에러 로그를 확인해서, 코드를 수정해야 할 것이다.
References
https://www.vlinkinfo.com/blog/tips-to-lower-cpu-usage-in-dot-net-applications/
'인프라 > Monitoring' 카테고리의 다른 글
Loki 로 로그 저장하고, Grafana 로 시각화하기 (0) | 2025.01.02 |
---|---|
그라파나 - 대시보드 템플릿 사용하기 (0) | 2024.12.27 |
Grafana (0) | 2024.12.24 |
Prometheus (0) | 2024.12.23 |
모니터링 - Spring Actuator (1) | 2024.12.20 |