반응형

JVM Data Area

1. Method Area (메서드 영역)

설명:

  • 클래스 수준의 정보가 저장되는 공간이다.
  • 클래스 로딩 시점에 JVM에 의해 로드되고, 클래스가 언로드될 때까지 유지된다.

저장되는 데이터:

  • 클래스 이름, 부모 클래스 이름, 메서드 정보, 필드 정보
  • static 변수
  • constant pool (상수 풀)

사용 시점:

  • 클래스가 처음 로드될 때 해당 정보들이 이 영역에 저장된다.

설정 방법:

  • 이 영역은 PermGen (Java 7 이하) 또는 Metaspace (Java 8 이상)로 구성된다.
  • 설정 예:
    • Java 7 이하: XX:PermSize, XX:MaxPermSize
    • Java 8 이상: XX:MetaspaceSize, XX:MaxMetaspaceSize

 

2. Heap Area (힙 영역)

설명:

  • 자바에서 생성된 객체가 저장되는 영역이다.
  • GC(Garbage Collector)의 대상이 되는 주요 영역이다.

저장되는 데이터:

  • 인스턴스 변수
  • 배열
  • 객체 데이터

사용 시점:

  • new 키워드를 통해 객체가 생성될 때마다 힙에 저장된다.

설정 방법:

  • Xms (초기 힙 크기), Xmx (최대 힙 크기) 옵션으로 조절 가능
  • bash 복사편집 java -Xms256m -Xmx1024m MyApp

 

3. Stack Area (스택 영역)

설명:

  • 각 스레드마다 생성되며, 메서드 호출 시 프레임(Stack Frame)을 저장하는 공간이다.

저장되는 데이터:

  • 지역 변수
  • 매개 변수
  • 연산 중간 결과
  • 리턴 값
  • 메서드 호출 시의 정보 (프레임)

사용 시점:

  • 메서드가 호출될 때마다 새로운 스택 프레임이 생성되고, 메서드가 종료되면 제거된다.

설정 방법:

  • Xss 옵션으로 각 스레드의 스택 크기를 설정 가능
  • bash 복사편집 java -Xss512k MyApp

 

4. Program Counter (PC) Register

설명:

  • 각 스레드마다 존재하며, 현재 실행 중인 JVM 명령어의 주소를 저장한다.

저장되는 데이터:

  • 현재 실행 중인 명령어의 주소 (bytecode 위치)

사용 시점:

  • JVM 명령어가 실행될 때, 다음에 실행할 명령어의 위치를 추적한다.

설정 방법:

  • JVM이 내부적으로 관리하며, 별도의 설정은 제공하지 않는다.

 

5. Native Method Stack (네이티브 메서드 스택)

설명:

  • 자바가 아닌 네이티브 코드(C, C++)를 호출할 때 사용되는 스택이다.
  • 예: System.gc() 같은 메서드는 내부적으로 네이티브 메서드를 호출한다.

저장되는 데이터:

  • 네이티브 메서드 호출에 필요한 정보와 지역 변수

사용 시점:

  • native 키워드가 선언된 메서드가 호출될 때

설정 방법:

  • 일반적으로 설정하지 않지만, JVM 구현체에 따라 스택 크기를 조절할 수 있다.

 

요약

영역 저장되는 데이터 생성 시점 주요 설정

Method Area 클래스 정보, static, 상수 풀 클래스 로딩 시 -XX:MetaspaceSize, -XX:MaxMetaspaceSize
Heap 객체, 배열 객체 생성 시 -Xms, -Xmx
Stack 지역 변수, 프레임 메서드 호출 시 -Xss
PC Register 명령어 위치 스레드 시작 시 (설정 불가)
Native Method Stack 네이티브 호출 정보 native 메서드 호출 시 JVM 의존적

필요한 경우 GC 튜닝이나 OutOfMemoryError 대응을 위해 이 영역들의 크기를 조정하게 된다. 각 영역은 JVM이 스레드 및 애플리케이션 실행 상태를 관리하기 위한 필수 구성 요소이므로, 시스템 성능 및 안정성에 영향을 줄 수 있다.

 

 

반응형
반응형

서블릿 컨테이너란?

서블릿(Servlet)은 자바로 작성된 서버 측 프로그램으로, 주로 HTTP 요청과 응답을 처리하는 데 사용된다. 클라이언트가 보낸 요청을 받아 서버에서 필요한 로직을 수행하고, 그 결과를 다시 클라이언트에게 응답하는 역할을 한다.

이러한 서블릿을 실행하고 관리하는 환경을 제공하는 것이 바로 서블릿 컨테이너(Servlet Container)다. 서블릿 컨테이너는 서블릿의 생명 주기를 관리하며, 클라이언트의 요청을 적절한 서블릿에 전달하고, 서블릿이 생성한 응답을 다시 클라이언트에게 전달하는 중심적인 역할을 수행한다.

 

 

서블릿 컨테이너의 주요 역할

 

  • 서블릿 객체의 생성과 소멸 관리
    서블릿 컨테이너는 요청이 처음 들어올 때 서블릿 객체를 생성하고, 더 이상 필요하지 않을 경우 이를 메모리에서 해제하는 방식으로 자원을 효율적으로 관리한다.
  • HTTP 요청과 서블릿 매핑
    클라이언트로부터 들어온 요청의 URL을 분석하여 어떤 서블릿이 이 요청을 처리할지 결정하고, 해당 서블릿의 service 메서드를 호출한다.
  • 요청 및 응답 객체 생성
    요청 정보를 담은 HttpServletRequest 객체와, 응답 작성을 위한 HttpServletResponse 객체를 생성하여 서블릿에 전달한다.
  • 멀티쓰레딩 지원
    각 클라이언트 요청마다 별도의 쓰레드를 생성해 서블릿을 실행함으로써 여러 사용자의 요청을 동시에 처리할 수 있도록 한다.
  • 보안 및 세션 관리
    인증, 권한 검사, HTTPS 처리, 세션 유지 등 웹 애플리케이션에 필요한 다양한 기능을 제공한다.

 

 

대표적인 서블릿 컨테이너

서블릿 컨테이너는 별도의 서버로 운영되거나 애플리케이션에 내장될 수 있다. 대표적인 서블릿 컨테이너는 다음과 같다.

  • Tomcat
  • Jetty
  • Undertow
  • WildFly

Spring Boot는 기본적으로 Tomcat을 내장 서버로 사용하며, 실행 시 자동으로 서블릿 컨테이너가 함께 구동된다.

 

 

Spring Boot와의 관계

Spring Boot 애플리케이션도 내부적으로는 DispatcherServlet이라는 서블릿을 기반으로 동작한다. 이 DispatcherServlet은 서블릿 컨테이너에 의해 관리되며, 클라이언트의 요청을 받아 적절한 컨트롤러로 분기하고, 그 결과를 응답으로 구성해 반환한다.

결국 Spring Boot 애플리케이션도 서블릿 컨테이너 위에서 동작한다고 볼 수 있으며, Spring의 많은 기능들은 이 서블릿 기반 구조를 바탕으로 구현된다.

 

 

 

 

 

반응형
반응형

Resolver란?

Resolver는 요청이나 데이터를 해석하여 필요한 객체나 값으로 변환하거나 찾아주는 컴포넌트를 의미합니다.

쉽게 말해, 클라이언트가 보낸 요청이나 특정 데이터를 내부적으로 필요한 형태(예: 객체, 값 등)로 가공하거나 찾아주는 역할을 합니다.
"Resolve"라는 단어 자체가 "해결하다", "변환하다"는 뜻을 가지고 있기 때문에, Resolver는 어떤 것을 해석해서 알맞은 형태로 바꿔주는 기능이라고 이해하면 됩니다.

 

Spring Boot에서의 Resolver 의미와 종류

1. HandlerMethodArgumentResolver (HTTP 요청 파라미터 해석기)

Spring MVC에서 컨트롤러 메서드의 파라미터를 해석해서 값으로 주입하는 컴포넌트입니다.
예를 들어, 다음과 같은 컨트롤러 메서드를 생각해봅시다.

@GetMapping("/user")
public String getUser(@CurrentUser User user) { 
	return user.getName();
 }

여기서 @CurrentUser는 커스텀 어노테이션이고, 이 값을 주입하려면 HandlerMethodArgumentResolver를 구현해야 합니다.

예)

public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(CurrentUser.class) != null &&
               parameter.getParameterType().equals(User.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) {
        // 실제 사용자 객체를 반환
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
}

이후 WebMvcConfigurer에서 등록해 줍니다:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CurrentUserArgumentResolver());
    }
}
 

2. ViewResolver (뷰 이름을 템플릿으로 매핑)

Spring MVC에서 컨트롤러가 반환한 뷰 이름을 실제 뷰(template)로 변환하는 역할입니다.

@Controller
public String hello(Model model) {
    model.addAttribute("msg", "Hello");
    return "greeting"; // ViewResolver가 greeting.html로 연결
}

 

반응형
반응형

Apache Kafka란?

1. Kafka를 이해하기 위한 시작점

오늘날처럼 데이터가 폭발적으로 생성되는 시대에는, 실시간으로 데이터를 수집하고 처리할 수 있는 시스템이 필수입니다. 바로 이 역할을 수행하는 것이 Apache Kafka입니다.

Kafka는 분산형 스트리밍 플랫폼입니다. 간단히 말하면, 다양한 시스템이나 서비스들이 데이터를 주고받을 때 그 중간에서 데이터를 안전하게 전달하고 저장해주는 중개자 역할을 합니다. 특히 초당 수백만 건의 메시지를 처리할 수 있을 정도로 높은 처리량을 자랑합니다.

Kafka는 LinkedIn에서 내부 로그 처리 시스템으로 시작했으며, 이후 오픈소스로 공개되었고, 현재는 전 세계 수많은 기업에서 사용되고 있습니다.


2. Kafka는 왜 필요한가?

Kafka가 없던 시절을 생각해봅시다. 예를 들어, A라는 서비스에서 생성된 데이터를 B, C, D 시스템이 각각 받아야 한다고 가정해보죠. A가 B, C, D 각각에 데이터를 직접 전달하려면 다음과 같은 문제가 발생합니다.

  • 연결이 복잡해짐: A는 세 시스템에 모두 연결해야 하며, 시스템이 늘어날수록 더 복잡해집니다.
  • 오류에 취약함: 만약 C 시스템이 잠시 다운되면 A는 데이터를 어떻게 처리해야 할까요?
  • 확장에 한계가 있음: 시스템이 많아질수록 성능이 떨어지고, 유지보수가 어려워집니다.

Kafka는 이런 문제들을 해결해줍니다. 데이터는 A에서 Kafka에 한 번만 보내면 되고, B, C, D는 Kafka에서 데이터를 받아가면 됩니다. 즉, 생산자(Producer)와 소비자(Consumer)를 완전히 분리할 수 있게 되는 것이죠.


3. Kafka의 기본 구성 요소

Kafka를 구성하는 핵심 개념은 아래와 같습니다.

1) Producer

데이터를 Kafka로 보내는 주체입니다. 예를 들어, 웹 서버에서 사용자 행동 로그를 Kafka로 전송하는 것이 이에 해당합니다.

2) Consumer

Kafka에서 데이터를 가져가는 주체입니다. 예를 들어, 분석 시스템이 Kafka에서 사용자 로그를 수신해 분석하는 식이죠.

3) Broker

Kafka 서버입니다. 데이터가 이곳에 저장되고, Producer와 Consumer는 이 Broker를 통해 데이터를 주고받습니다. Kafka는 일반적으로 여러 대의 Broker로 구성되어 있으며, 이들로 클러스터를 구성합니다.

4) Topic

Kafka에서 데이터는 Topic이라는 단위로 구분됩니다. Topic은 일종의 카테고리라고 생각하면 됩니다. 예를 들어, “user-login”이라는 Topic에는 로그인 로그만 모이게 할 수 있습니다.

5) Partition

Topic은 하나 이상의 Partition으로 나뉘어 저장됩니다. 각 Partition은 독립적으로 동작하며, 덕분에 Kafka는 병렬로 데이터를 처리할 수 있고, 성능이 매우 뛰어납니다.


4. Kafka는 어떻게 작동하나?

Kafka의 데이터 흐름을 간단히 정리해보면 아래와 같습니다.

  1. Producer가 특정 Topic으로 데이터를 전송합니다.
  2. Kafka는 이 데이터를 해당 Topic의 Partition에 저장합니다.
  3. Consumer는 원하는 Topic을 구독(subscribe)하여 데이터를 가져갑니다.
  4. Consumer는 자신이 어디까지 읽었는지를 Kafka에 알려주고, 그 이후부터 계속 데이터를 읽습니다.

중요한 점은, Kafka는 데이터를 일정 기간 동안 저장해준다는 점입니다. 일반적인 메시지 큐와는 달리, Kafka는 소비자가 데이터를 읽었는지 여부에 상관없이 데이터를 일정 기간 유지합니다(기본 7일). 이를 통해 같은 데이터를 여러 소비자가 서로 독립적으로 읽을 수 있게 됩니다.


5. Kafka의 강점과 활용 사례

Kafka는 다음과 같은 장점이 있습니다.

  • 높은 처리량: 초당 수백만 건의 메시지를 처리할 수 있습니다.
  • 확장성: Partition을 늘리면 성능을 선형적으로 확장할 수 있습니다.
  • 내결함성: 복제(replication) 구조로 구성되어 장애에 강합니다.
  • 유연한 통합: 다양한 시스템과 손쉽게 연동할 수 있는 커넥터(Connector) 생태계를 갖추고 있습니다.

실무에서 Kafka는 어디에 쓰일까?

  • 로그 수집 시스템: 수천 대의 서버에서 쏟아지는 로그를 Kafka로 수집해 ELK(Elasticsearch, Logstash, Kibana)로 분석.
  • 실시간 분석: 사용자 행동 데이터를 Kafka로 전송하고, 실시간 분석 플랫폼(예: Apache Flink)에서 분석.
  • 모니터링 시스템: 마이크로서비스 아키텍처에서 서비스 간 상태 정보를 Kafka로 전송하여 모니터링 대시보드에 표시.
  • 이벤트 기반 시스템: 주문, 결제, 배송 같은 비즈니스 이벤트를 Kafka를 통해 연결.

 

 

 

 

 

반응형
반응형

카프카 커넥트 역활

  • 소스 시스템의 데이터를 카프카 커넥트를 통해 카프카로 전송합니다.
  • 카프카에 저장된 데이터는 카프카 커넥트를 통해 싱크 시스템으로 전송합니다.
  • 카프카 커넥트가 데이터 전송 처리하는 역할을 담당합니다.

 

카프카 커넥트 기본 용어

  • 커넥터(connector): 외부 시스템과 카프카 커넥트 런타임 간의 인터페이스 역할, 태스크들을 관리하며, 소스 커넥터와 싱크 커넥터로 구분
  • 태스크(task): 실제 카프카와 시스템 사이에서 데이터를 가져오거나 보내는 작업을 처리
  • 워커(worker): 커넥터 및 태스크를 실행하는 프로세스
  • 컨버터(converter): 커넥터와 데이터를 보내거나 받는 시스템 간의 데이터 포맷 간에 레코드를 시리얼라이즈 또는 디시리얼라이즈 처리
  • 트랜스폼(transform): 레코드를 보내거나 받을 때 레코드를 간단한 로직으로 변환

 

카프카 커넥트 동작

  • 다양한 종류의 커넥터가 존재합니다.
  • 커넥터들은 Confluent Hub에서 확인 가능합니다.

 

카프카 커넥트 동작 예시

데이터베이스에서 지속적으로 생성되는 데이터를 HDFS로 전송해야 하는 상황입니다. 이 작업은 두 단계로 나누어 진행됩니다.

  1. 데이터베이스 → Kafka
  2. Kafka → HDFS

1 단계: 데이터베이스에서 Kafka로 데이터 전송

첫 번째 단계에서는 Kafka Connect의 JDBC 커넥터를 활용할 수 있습니다. JDBC 커넥터는 소스 데이터베이스에서 변경 데이터를 추출하여 Kafka로 전송하는 역할을 합니다.

아래 그림과 같이, 커넥터를 생성하기 위해 필요한 설정 값을 입력한 후 REST API를 호출하면, 커넥터가 즉시 생성됩니다.

JDBC 커넥터는 내부적으로 데이터를 수집하는 **태스크(Task)**를 실행하며, 이 태스크들은 커넥터가 관리합니다.
필요에 따라 tasks.max 옵션을 설정하여 실행할 태스크의 수를 조절할 수 있습니다.

 

2 단계: Kafka에서 HDFS로 데이터 전송

두 번째 단계에서는 Kafka Connect의 HDFS 커넥터를 사용합니다. HDFS 커넥터는 Kafka에 저장된 데이터를 HDFS로 전송하는 역할을 합니다.

JDBC 커넥터와 마찬가지로, HDFS 커넥터에 필요한 설정 값을 입력한 후 REST API를 호출하면 커넥터가 생성되며, Kafka 토픽의 데이터를 HDFS에 저장하기 시작합니다.

 

 

반응형
반응형

Kafka Zero Copy: 데이터 전송 효율을 극대화하다

최근 데이터 스트리밍 시장에서 Kafka의 인기가 높아짐에 따라, 그 내부 기술에 대한 관심도 커지고 있습니다. 오늘은 Kafka의 핵심 성능 최적화 기법 중 하나인 Zero Copy에 대해 이야기해보고자 합니다.

 

Zero Copy, 그게 뭐야?

 

우리가 보통 데이터를 전송할 때는 CPU가 여러 번 데이터를 복사하는 과정을 거치게 됩니다. 이 과정은 특히 대용량 데이터를 처리할 때 큰 부담이 될 수 있죠. Zero Copy는 이런 복사 과정을 최소화하여, 데이터를 직접 네트워크나 디스크로 전송하는 기술입니다. 이렇게 하면 CPU의 부담이 줄어들고, 전체적인 시스템 성능이 향상됩니다.

 

Kafka에서 Zero Copy가 어떻게 활용될까?

Kafka는 대용량의 메시지를 빠르게 처리해야 하는 특성이 있습니다. 그래서 Kafka는 디스크에 저장된 데이터를 클라이언트에게 전송할 때, 커널의 sendfile 시스템 콜을 이용해 데이터를 복사하지 않고 바로 전송합니다. 이러한 접근 방식 덕분에 Kafka는 CPU 사용을 크게 줄이고, 데이터 처리량을 극대화할 수 있었습니다.

 

Kafka Zero Copy (sendfile 사용) 방식

       +----------+
       |   Disk   |
       +----------+
            │
            │  sendfile (커널 내 직접 전송)
            ▼
   +---------------------+
   |  Kernel (sendfile)  |
   +---------------------+
            │
            ▼
       +-----------+
       |  Network  |
       +-----------+

 

이 방식에서는 데이터가 디스크에서 바로 커널 내부의 네트워크 스택으로 전송되므로, 사용자 공간으로의 불필요한 데이터 복사가 발생하지 않습니다.

 

Zero Copy 미사용 방식 (일반적인 데이터 전송 절차)

       +----------+
       |   Disk   |
       +----------+
            │
            │  데이터 읽기
            ▼
       +--------------+
       |  User Space  |  (버퍼에 복사)
       +--------------+
            │
            │  데이터 쓰기
            ▼
   +--------------+
   | Kernel Space |  (또 한 번 복사)
   +--------------+
            │
            ▼
       +-----------+
       |  Network  |
       +-----------+

이 경우, 데이터는 디스크에서 먼저 사용자 공간으로 복사되고, 다시 커널 공간으로 복사되어 네트워크로 전송되므로 불필요한 메모리 복사 오버헤드가 발생하게 됩니다.

 

 

왜 Zero Copy가 중요한가?

  • 빠른 처리 속도: 데이터 복사 오버헤드를 줄여, 많은 양의 데이터를 신속하게 처리할 수 있습니다.
  • 짧은 응답 시간: 데이터 전송 과정에서 불필요한 지연이 없어, 실시간 처리에 유리합니다.
  • 효율적인 시스템 운영: CPU 리소스를 효율적으로 사용하여 다른 작업에도 충분한 자원을 할당할 수 있습니다.
반응형
반응형

HTTP란 무엇이며, 어떤 특징을 가지는가?

 

HTTP(HyperText Transfer Protocol)는 웹에서 클라이언트와 서버 간 데이터를 주고받는 프로토콜로, 기본적으로 비연결형(Connectionless), 무상태(Stateless) 방식으로 동작합니다.

  • HTTP 1.1: Keep-Alive 기능으로 다중 요청 가능
  • HTTP 2: 멀티플렉싱(Multiplexing) 지원, 헤더 압축
  • HTTP 3: UDP 기반의 QUIC 프로토콜을 사용하여 속도 개선
반응형

'IT' 카테고리의 다른 글

대규모 서비스의 데이터 폭발과 처리 전략  (0) 2025.03.16
DDD  (0) 2024.12.07
Service API 개발 할 때 고려할 사항  (1) 2024.12.06
Build, Deploy, Complie  (0) 2023.06.13
WEB-INF와 META-INF 차이  (0) 2023.06.13
반응형

Trie를 이용한 접두사 검색 (Autocomplete 기능 구현)

 

import java.util.*;

class TrieNode {
    TrieNode[] children;
    boolean isEndOfWord;

    public TrieNode() {
        children = new TrieNode[26]; // 알파벳 소문자 기준
        isEndOfWord = false;
    }
}

class Trie {
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    public void insert(String word) {
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            int index = c - 'a';
            if (node.children[index] == null) {
                node.children[index] = new TrieNode();
            }
            node = node.children[index];
        }
        node.isEndOfWord = true;
    }

    public List<String> getWordsWithPrefix(String prefix) {
        List<String> result = new ArrayList<>();
        TrieNode node = root;
        for (char c : prefix.toCharArray()) {
            int index = c - 'a';
            if (node.children[index] == null) {
                return result;
            }
            node = node.children[index];
        }
        findWords(node, new StringBuilder(prefix), result);
        return result;
    }

    private void findWords(TrieNode node, StringBuilder prefix, List<String> result) {
        if (node.isEndOfWord) {
            result.add(prefix.toString());
        }
        for (char c = 'a'; c <= 'z'; c++) {
            int index = c - 'a';
            if (node.children[index] != null) {
                prefix.append(c);
                findWords(node.children[index], prefix, result);
                prefix.deleteCharAt(prefix.length() - 1);
            }
        }
    }
}
반응형

'Java' 카테고리의 다른 글

[Java]JVM Data Area  (0) 2025.05.21
Java 7 functions  (0) 2023.06.18
JDK, JRE, JVM이란?  (0) 2023.06.15
[ERROR]cannot find symbol  (0) 2023.05.29
[ERROR]Illegal modifier for the interface field Observer.name; only public, static & final are permitted  (0) 2023.05.29

+ Recent posts