Camel 성능 문제의 일반적인 원인
Camel 애플리케이션의 성능 문제는 대개 세 가지 원인에서 비롯됩니다. 첫째는 외부 시스템의 느린 응답, 둘째는 메모리에 큰 메시지를 통째로 올리는 것, 셋째는 스레드 풀 설정 미스입니다.
스트리밍으로 대용량 파일 처리
수십 GB의 파일을 메모리에 통째로 올리면 OutOfMemoryError가 발생합니다. Camel의 스트리밍 처리를 활용하면 메모리를 최소화합니다.
// 대용량 CSV 파일을 줄 단위로 스트리밍 처리
from("file:large-files?noop=true")
.split(body().tokenize("
")).streaming()
.filter(body().isNotEqualTo("")) // 빈 줄 제거
.to("bean:csvRowProcessor")
.end();
streaming()을 추가하면 Camel이 전체 파일을 메모리에 올리지 않고 청크 단위로 처리합니다. 수 GB 파일도 수십 MB 메모리로 처리할 수 있습니다.
스레드 풀 최적화
기본 스레드 풀 크기는 모든 상황에 최적이 아닙니다. IO 바운드 작업은 스레드를 많이, CPU 바운드 작업은 CPU 코어 수에 맞게 설정합니다.
// ThreadPoolProfile로 기본값 변경
ThreadPoolProfile profile = new ThreadPoolProfileBuilder("io-bound")
.poolSize(20) // IO 바운드: CPU 코어 * 2~4
.maxPoolSize(100)
.maxQueueSize(1000)
.keepAliveTime(60, TimeUnit.SECONDS)
.build();
context.getExecutorServiceManager().registerThreadPoolProfile(profile);
메시지 배치 처리로 처리량 향상
DB 쿼리나 외부 API 호출을 건별로 하는 것보다 배치로 묶으면 오버헤드를 크게 줄일 수 있습니다.
// 100개 또는 5초마다 배치 처리
from("activemq:queue:items")
.aggregate(constant(true), new GroupedBodyAggregationStrategy())
.completionSize(100)
.completionTimeout(5000)
.to("bean:batchInsertService"); // 한 번에 100개 INSERT
캐싱으로 반복 조회 최적화
자주 조회하지만 잘 변경되지 않는 데이터는 캐시를 활용합니다.
from("direct:getProductInfo")
.setHeader("cacheKey", simple("product-${body}"))
.to("cache://productCache?operation=GET")
.choice()
.when(header(CacheConstants.CACHE_ELEMENT_WAS_FOUND))
.log("캐시 히트")
.otherwise()
.to("sql:SELECT * FROM products WHERE id = :#${body}")
.to("cache://productCache?operation=ADD")
.end();
성능 프로파일링
어떤 라우트, 어떤 단계가 느린지 파악하려면 성능 측정이 먼저입니다.
// 각 단계별 처리 시간 측정
from("direct:order")
.log("주문 처리 시작")
.process(exchange -> {
long start = System.currentTimeMillis();
// 처리
long elapsed = System.currentTimeMillis() - start;
exchange.getIn().setHeader("processingTime", elapsed);
})
.to("micrometer:timer:order.processing.time?action=stop");
Micrometer를 통해 각 단계의 처리 시간이 Prometheus로 수집되면, Grafana에서 95th percentile 응답 시간 같은 세밀한 분석이 가능합니다.