• 티스토리 홈
  • 프로필사진
    알쓸개잡
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
알쓸개잡
  • 프로필사진
    알쓸개잡
    • 분류 전체보기 (92)
      • 스프링부트 (52)
      • AWS (5)
      • 쿠버네티스 (7)
      • 자바 (19)
      • 인프라 (0)
      • ETC (8)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      • 반응형
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • spring boot application startup tracking
        2023년 09월 17일
        • 알쓸개잡
        • 작성자
        • 2023.09.17.:24

        spring boot 애플리케이션을 시작하는 동안 SpringApplication과 ApplicationContext는 애플리케이션 라이프사이클, 빈 라이프사이클, 애플리케이션 이벤트 처리와 관련된 많은 작업을 수행한다. Spring은 ApplicationStartup을 통해서 StartupStep 객체를 사용하여 애플리케이션 시작 과정을 추적할 수 있다. 추적 데이터는 프로파일링 목적으로 수집하거나 애플리케이션 시작 프로세스를 이해하는데 도움이 될 수 있다. spring boot 애플리케이션 시작 과정에 대한 추적 데이터를 얻는 방법에 대해서 기록한다.

         

        Spring Boot 2.4부터 ApplicationStartup 메트릭을 생성할 수 있게 되었다. 해당 메트릭에는 시작 과정에서 생성되는 각각의 이벤트와 라이프사이클이 처리되는 소요 시간 정보도 포함되어 있어 어느 부분에서 지연이 발생되는지도 확인할 수 있다.

         

        Startup 추적 데이터 형식

        Startup 추적 데이터는 애플리케이션이 실행되는 전체 과정을 여러 스텝으로 나누어 표시한다. 각 스텝은 아이디, 이름, 상위 아이디, 시작시간, 종료시간, 소유시간 정보를 포함한다.

        추적 데이터 형식은 다음과 같다.

        {
            "springBootVersion": "3.1.3",
            "timeline": {
                "startTime": "2023-09-16T12:27:09.491465Z",
                "events": [
                    {
                        "endTime": "2023-09-16T12:27:09.507801Z",
                        "duration": "PT0.01286S",
                        "startTime": "2023-09-16T12:27:09.494941Z",
                        "startupStep": {
                            "name": "spring.boot.application.starting",
                            "id": 0,
                            "tags": [
                                {
                                    "key": "mainApplicationClass",
                                    "value": "com.example.defaultport.DefaultPortApplication"
                                }
                            ]
                        }
                    },
                    ...
                    {
                        "endTime": "2023-09-16T12:27:10.268548Z",
                        "duration": "PT0.000164S",
                        "startTime": "2023-09-16T12:27:10.268384Z",
                        "startupStep": {
                            "name": "spring.boot.application.ready",
                            "id": 311,
                            "tags": []
                        }
                    }
                ]
            }
        }

         

        Startup 추적 데이터 수집 하기

        Startup 추적 데이터 수집을 활성화하기 위해서는 Spring Boot 버전 2.4 이상부터 가능하다.

         

        pom.xml

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.1.3</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        
        <dependencies>
        ...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        ...
        </dependencies>

         

        SpringApplication 인스턴스에 setApplicationStartup 설정을 한다.

        public static void main(String[] args) {
            SpringApplication application = new SpringApplication(DefaultPortApplication.class);
            application.setApplicationStartup(new BufferingApplicationStartup(10000));
            application.run(args);
        }

        setApplicationStartup 메서드는 ApplicationStartup 타입의 인스턴스를 인자로 전달받는다.

        BufferingApplicationStartup(int capacity) 생성자는 capacity를 전달받는데 이는 Step의 개수만큼만 버퍼에 담아서 처리한다는 것이다. (추적 데이터의 byte 길이가 아니라는 것에 유의) Step의 개수가 capacity를 초과하면 추적되지 않는다.

        위 코드는 최대 10000개 까지의 Step을 추적하도록 설정하였다.

         

        애플리케이션 Startup 추적 데이터는 actuator의 /startup 엔드포인트에서 모니터링할 수 있다. 이를 위해서는 actuator의 /startup 엔드포인트를 활성화해야 한다.

        management:
          endpoints:
            web:
              exposure:
                include: 'startup'

        준비는 끝났다. 애플리케이션을 실행 후 Postman에서 POST /actuator/startup 엔드포인트를 호출해 보았다.

        /actuator/startup 엔드포인트 호출

        결과는 아래와 같은 형식으로 출력된다.

        {
            "springBootVersion": "3.1.3",
            "timeline": {
                "startTime": "2023-09-16T12:27:09.491465Z",
                "events": [
                    {
                        "endTime": "2023-09-16T12:27:09.507801Z",
                        "duration": "PT0.01286S",
                        "startTime": "2023-09-16T12:27:09.494941Z",
                        "startupStep": {
                            "name": "spring.boot.application.starting",
                            "id": 0,
                            "tags": [
                                {
                                    "key": "mainApplicationClass",
                                    "value": "com.example.defaultport.DefaultPortApplication"
                                }
                            ]
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:09.612995Z",
                        "duration": "PT0.081873S",
                        "startTime": "2023-09-16T12:27:09.531122Z",
                        "startupStep": {
                            "name": "spring.boot.application.environment-prepared",
                            "id": 1,
                            "tags": []
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:09.641478Z",
                        "duration": "PT0.000117S",
                        "startTime": "2023-09-16T12:27:09.641361Z",
                        "startupStep": {
                            "name": "spring.boot.application.context-prepared",
                            "id": 2,
                            "tags": []
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:09.665428Z",
                        "duration": "PT0.000868S",
                        "startTime": "2023-09-16T12:27:09.664560Z",
                        "startupStep": {
                            "name": "spring.boot.application.context-loaded",
                            "id": 3,
                            "tags": []
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:09.678599Z",
                        "duration": "PT0.000524S",
                        "startTime": "2023-09-16T12:27:09.678075Z",
                        "startupStep": {
                            "name": "spring.beans.instantiate",
                            "id": 7,
                            "tags": [
                                {
                                    "key": "beanName",
                                    "value": "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"
                                }
                            ],
                            "parentId": 6
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:09.681786Z",
                        "duration": "PT0.008426S",
                        "startTime": "2023-09-16T12:27:09.673360Z",
                        "startupStep": {
                            "name": "spring.beans.instantiate",
                            "id": 6,
                            "tags": [
                                {
                                    "key": "beanName",
                                    "value": "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
                                },
                                {
                                    "key": "beanType",
                                    "value": "interface org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor"
                                }
                            ],
                            "parentId": 5
                        }
                    },
                    ...
                    {
                        "endTime": "2023-09-16T12:27:09.936435Z",
                        "duration": "PT0.26572S",
                        "startTime": "2023-09-16T12:27:09.670715Z",
                        "startupStep": {
                            "name": "spring.context.beans.post-process",
                            "id": 5,
                            "tags": [],
                            "parentId": 4
                        }
                    },
                    ...
                    {
                        "endTime": "2023-09-16T12:27:10.100159Z",
                        "duration": "PT0.162986S",
                        "startTime": "2023-09-16T12:27:09.937173Z",
                        "startupStep": {
                            "name": "spring.boot.webserver.create",
                            "id": 36,
                            "tags": [
                                {
                                    "key": "factory",
                                    "value": "class org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory"
                                }
                            ],
                            "parentId": 4
                        }
                    },
                    ...
                    {
                        "endTime": "2023-09-16T12:27:10.244112Z",
                        "duration": "PT0.003741S",
                        "startTime": "2023-09-16T12:27:10.240371Z",
                        "startupStep": {
                            "name": "spring.beans.smart-initialize",
                            "id": 305,
                            "tags": [
                                {
                                    "key": "beanName",
                                    "value": "org.springframework.context.event.internalEventListenerProcessor"
                                }
                            ],
                            "parentId": 4
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:10.244221Z",
                        "duration": "PT0.000087S",
                        "startTime": "2023-09-16T12:27:10.244134Z",
                        "startupStep": {
                            "name": "spring.beans.smart-initialize",
                            "id": 306,
                            "tags": [
                                {
                                    "key": "beanName",
                                    "value": "healthEndpointGroupMembershipValidator"
                                }
                            ],
                            "parentId": 4
                        }
                    },
                    ...
                    {
                        "endTime": "2023-09-16T12:27:10.265704Z",
                        "duration": "PT0.600247S",
                        "startTime": "2023-09-16T12:27:09.665457Z",
                        "startupStep": {
                            "name": "spring.context.refresh",
                            "id": 4,
                            "tags": []
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:10.268021Z",
                        "duration": "PT0.0014S",
                        "startTime": "2023-09-16T12:27:10.266621Z",
                        "startupStep": {
                            "name": "spring.boot.application.started",
                            "id": 310,
                            "tags": []
                        }
                    },
                    {
                        "endTime": "2023-09-16T12:27:10.268548Z",
                        "duration": "PT0.000164S",
                        "startTime": "2023-09-16T12:27:10.268384Z",
                        "startupStep": {
                            "name": "spring.boot.application.ready",
                            "id": 311,
                            "tags": []
                        }
                    }
                ]
            }
        }

        애플리케이션이 기동하면서 수행되는 애플리케이션 라이프사이클, 빈 라이프 사이클, 이벤트에 대해서 어떤 클래스에서 수행되고 소요시간을 얼마나 걸리는지 확인해 볼 수 있다. /actuator/startup 엔드포인트를 호출하면 버퍼에 기록된 Startup 추적데이터를 응답하고 버퍼를 비우기 때문에 그다음 /actuator/startup을 호출 시에는 빈 데이터를 응답한다.

        빈 데이터 응답

        Java Flight Recorder(JFR) 를 이용한 Startup 추적 데이터 수집

        JFR(Java Flight Recorder)은 JVM의 일부 모니터링 도구이다. Java는 다양한 메트릭 및 진단 데이터를 수집하고 이를 분석할 수 있는 도구를 제공한다. JFR을 통해서 Startup 추적 데이터를 확인하려면 FlightRecordingApplicationStartup을 사용해야 한다.

        public static void main(String[] args) {
            SpringApplication application = new SpringApplication(DefaultPortApplication.class);
            application.setApplicationStartup(new FlightRecorderApplicationStartup());
            application.run(args);
        }

        JFR을 사용하여 Startup 추적 데이터를 캡처하기 위해서 애플리케이션 실행 시 몇 가지 실행 옵션을 전달해야 한다.

        java -XX:+FlightRecorder -XX:StartFlightRecording=duration=5s,filename=startup.jfr \
        -jar target/default-port-0.0.1-SNAPSHOT.jar

        위 명령을 실행하면 위 명령을 실행한 경로에 startup.jfr 파일이 생성된다. intellij를 통해서 jfr 파일을 확인할 수도 있고 JMC(Java Mission Control) 도구를 설치하여 jfr 파일을 확인할 수 있다.

        JMC 도구는 링크로 이동하여 다운로드 받을 수 있다.

         

        Intellij 에서 jfr 파일 분석
        JMC를 통한 jfr 파일 분석

         

        Spring Boot 애플리케이션 실행 흐름에 대한 추적 데이터를 생성하는 방법에 대해서 알아보았다. 

        Spring Boot 애플리케이션의 시작 루틴에 대해서 프로파일링을 통해서 Spring Boot의 시작흐름을 파악하고 시작 과정에서 지연되는 지점을 파악하는데 도움이 될 것 같다.

        저작자표시 비영리 변경금지 (새창열림)

        '스프링부트' 카테고리의 다른 글

        spring boot actuator health endpoints delay check  (0) 2023.09.22
        spring boot websocket (웹소켓)  (0) 2023.09.20
        Spring boot embedded server 의 default 포트 변경  (0) 2023.09.16
        Spring에서 Jasypt를 이용하여 설정 정보 암호화 하기  (0) 2023.09.09
        Spring SSE (Server Sent Event) 사용 방법  (0) 2023.09.09
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바