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

        spring boot actuator의 health check는 해당 서비스의 라이브 상태만을 체크 하는 것이 아니라 해당 서비스가 사용하고 있는 각종 endpoint들의 라이브 상태도 체크를 한다. 여기서 endpoint라는 것은 해당 서비스가 사용하고 있는 DB, Kafka, Redis, JMS와 같은 외부 인프라 서비스를 의미한다. 간혹 health check 수행 시간에 지연이 발생 하는 경우 어떤 endpoint에서 지연이 발생 하는지 확인이 필요한 경우가 있다. 이번 포스팅에서는 health check 대상 endpoint의 health check 수행 시간을 로그에 기록하도록 설정하는 방법에 대해서 기록한다.

         

        HealthContributer (HealthIndicator)

        spring boot actuator의 각 endpoint들의 health check는 HealthContributor(HealthIndicator)를 구현한 구현체 내에서 이루어진다. actuator에서 제공하는 contributor는 org.springframework.boot:spring-boot-actuator-autoconfigure 아티팩트의 org.springframework.boot.actuate.autoconfigure 패키지내에 있는 각 XXXXAutoConfiguration에 의해서 자동 빈으로 등록된다. 이때 빈으로 등록될 contributor 대상은 관련 서비스의 빈 생성 여부에 따라서 정해지는 듯 하다.

        자동으로 주입되는 health contributor(indicator)는 spring actuator 문서에서 확인할 수 있다.

         

        Mail contributor 생성에 대한 예를 들어 보겠다.

        org.springframework.boot.actuate.autoconfigure.mail 패키지에 있는 MailHealthContributorAutoConfiguration 클래스는 아래와 같다.

        @AutoConfiguration(after = MailSenderAutoConfiguration.class)
        @ConditionalOnClass(JavaMailSenderImpl.class)
        @ConditionalOnBean(JavaMailSenderImpl.class)
        @ConditionalOnEnabledHealthIndicator("mail")
        public class MailHealthContributorAutoConfiguration
        		extends CompositeHealthContributorConfiguration<MailHealthIndicator, JavaMailSenderImpl> {
        
        	public MailHealthContributorAutoConfiguration() {
        		super(MailHealthIndicator::new);
        	}
        
        	@Bean
        	@ConditionalOnMissingBean(name = { "mailHealthIndicator", "mailHealthContributor" })
        	public HealthContributor mailHealthContributor(Map<String, JavaMailSenderImpl> mailSenders) {
        		return createContributor(mailSenders);
        	}
        
        }

        클래스 내용을 보면 MailSenderAutoConfiguration 이 수행된 이후에 MailHealthContributorAutoConfiguration이 수행된다.

        MailHealthContributorAutoConfiguration이 수행되는 조건은 아래와 같다.

        • JavaMailSenderImpl.class 가 존재해야 함 (@ConditionalOnClass(JavaMailSenderImpl.class))
        • JavaMailSenderImpl.class 가 빈으로 등록되어 있어야 함. (@ConditionalOnBean(JavaMailSenderImpl.class))
        • management.health.mail.enabled 설정이 true 로 설정되어 있어야 함. (@ConditionalOnEnabledHealthIndicator("mail"))
          • @ConditionalOnEnabledHealthIndicator(<name>) 의 경우 management.health.<name>.enabled 가 true로 설정되어야 <name>에 해당하는 서비스의 health check가 활성화 된다.
          • management.health.<name>.enabled 설정은 디폴트가 true이다. 
          • 이를 통해서 사용하고 있는 서비스의 health check contributor 대상에서 제외하려면 management.health.<name>.enabled 설정을 false로 지정하면 된다.

        MailSenderAutoConfiguration 클래스는 아래와 같다.

        @AutoConfiguration
        @ConditionalOnClass({ MimeMessage.class, MimeType.class, MailSender.class })
        @ConditionalOnMissingBean(MailSender.class)
        @Conditional(MailSenderCondition.class)
        @EnableConfigurationProperties(MailProperties.class)
        @Import({ MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class })
        public class MailSenderAutoConfiguration {
        
        	...
        
        }

        @Import({MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class}) 에 의해서 MailSnederJndiConfiguration.class, MailSenderPropertiesConfiguration.class Configuration이 실행되는데 MailSenderPropertiesConfiguration.class 는 아래와 같다.

        @Configuration(proxyBeanMethods = false)
        @ConditionalOnProperty(prefix = "spring.mail", name = "host")
        class MailSenderPropertiesConfiguration {
        
        	@Bean
        	@ConditionalOnMissingBean(JavaMailSender.class)
        	JavaMailSenderImpl mailSender(MailProperties properties) {
        		JavaMailSenderImpl sender = new JavaMailSenderImpl();
        		applyProperties(properties, sender);
        		return sender;
        	}
        
        	private void applyProperties(MailProperties properties, JavaMailSenderImpl sender) {
        		sender.setHost(properties.getHost());
        		if (properties.getPort() != null) {
        			sender.setPort(properties.getPort());
        		}
        		sender.setUsername(properties.getUsername());
        		sender.setPassword(properties.getPassword());
        		sender.setProtocol(properties.getProtocol());
        		if (properties.getDefaultEncoding() != null) {
        			sender.setDefaultEncoding(properties.getDefaultEncoding().name());
        		}
        		if (!properties.getProperties().isEmpty()) {
        			sender.setJavaMailProperties(asProperties(properties.getProperties()));
        		}
        	}
        
        	private Properties asProperties(Map<String, String> source) {
        		Properties properties = new Properties();
        		properties.putAll(source);
        		return properties;
        	}
        
        }

        @ConditionalOnProperty(prefix = "spring.mail", name = "host") 에 의해서 spring.mail.host 설정이 되어 있는 경우에 JavaMailSenderImpl 빈이 생성됨을 알 수 있다. MailProperties 클래스를 통해서 JavaMailSenderImpl 에 메일 관련 접속 설정 정보가 셋팅됨을 알 수 있다. 관련 설정 정보는 아래와 같다.

        • spring.mail.host
        • spring.mail.port
        • spring.mail.username
        • spring.mail.password
        • spring.mail.protocol
        • spring.mail.default-encoding
        • spring.mail.properties
        • spring.mail.jndi-name

        위 접속 정보를 통해서 actuator health check시에 mail contributor 에서 접속 테스트를 통해 mail에 대한 health check가 이루어질 것이다.

         

        정리하자면 mail endpoint의 contributor가 생성되는 조건은 아래와 같다.

        • spring.mail 관련 properties 설정 (JavaMailSenderImpl 빈 생성)
          • 직접 Configuration을 정의하여 JavaMailSender 빈을 생성하는 경우에는 JavaMailSender가 아닌 JavaMailSenderImpl 인스턴스 빈을 생성해야 한다.
        • management.health.mail.enabled=true (지정하지 않은 경우 디폴트 true 로 동작한다)

        위와 같이 contributor가 생성되는 조건이 충족이 되면 아래 코드가 실행이 된다.

        @Bean
        @ConditionalOnMissingBean(name = { "mailHealthIndicator", "mailHealthContributor" })
        public HealthContributor mailHealthContributor(Map<String, JavaMailSenderImpl> mailSenders) {
            return createContributor(mailSenders);
        }

        위 코드는 MailHealthIndicator를 생성하고 AutoConfiguredHealthContributorRegistry에 등록이 된다.

        AutoConfiguredHealthContributorRegistry 에는 health check 대상이 되는 여러 contributors 를 가지고 있고 AutoConfiguredHealthContributorRegistry 는 HealthEndpoint 빈에 등록이 된다. HealthEndpoint 빈은 HealthEndpointAutoConfiguration에 의해서 생성이 되는데 HealthEndpointAutoConfiguration은 아래와 같다.

        @AutoConfiguration
        @ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class)
        @EnableConfigurationProperties(HealthEndpointProperties.class)
        @Import({ HealthEndpointConfiguration.class, ReactiveHealthEndpointConfiguration.class,
        	HealthEndpointWebExtensionConfiguration.class, HealthEndpointReactiveWebExtensionConfiguration.class })
        public class HealthEndpointAutoConfiguration {
        
        }

        HealthEndpoint 빈은 HealthEndpointConfiguration 에서 생성되는데 코드는 아래와 같다.

        @Bean
        @ConditionalOnMissingBean
        HealthEndpoint healthEndpoint(HealthContributorRegistry registry, HealthEndpointGroups groups,
                HealthEndpointProperties properties) {
            return new HealthEndpoint(registry, groups, properties.getLogging().getSlowIndicatorThreshold());
        }

        위 코드에서 주입되는  HealthEndpointProperties 클래스를 보면 management.endpoint.health.xxx 설정 처리하는데 properties.getLogging().getSlowIndicatorThreshold() 호출은 management.endpoint.health.logging.slow-indicator-threshold에 설정된 값을 전달한다는 것을 알 수 있다. 이 설정 값은 각 contributor의 health check 소요 시간에 대한 임계치 역할을 한다.

         

        HealthEndpoint 클래스는 HealthEndpointSupport<HealthContributor, HealthComponent> 클래스를 상속 받는데 actuator health check 요청시 HealthEndpointSupport 클래스에서 각 contributor들을 순회하면서 health 정보를 가져 온다. HealthEndpointSupport 클래스의 getLoggedHealth 호출을 통해서 contributor의 health 정보를 가져오는데 getLoggedHealth 코드는 아래와 같다.

        private T getLoggedHealth(C contributor, String name, boolean showDetails) {
            Instant start = Instant.now();
            try {
                //contributor의 doHealthCheck가 호출된다.
                return getHealth(contributor, showDetails);
            }
            finally {
                //로그 레벨이 WARN으로 지정되어 있고 slow-indicator-logging-threshold 가 설정되어 있다면 수행
                if (logger.isWarnEnabled() && this.slowIndicatorLoggingThreshold != null) {
                    Duration duration = Duration.between(start, Instant.now());
                    //health check 수행 시간이 slow-indicator-logging-threshold 시간을 넘어선 경우
                    //로그를 생성한다. (slow-indicator-logging-threshold 디폴트는 10초이다)
                    if (duration.compareTo(this.slowIndicatorLoggingThreshold) > 0) {
                        String contributorClassName = contributor.getClass().getName();
                        Object contributorIdentifier = (!StringUtils.hasLength(name)) ? contributorClassName
                                : contributorClassName + " (" + name + ")";
                        logger.warn(LogMessage.format("Health contributor %s took %s to respond", contributorIdentifier,
                                DurationStyle.SIMPLE.print(duration)));
                    }
                }
            }
        }

        finally 블록에서 로그 레벨과 해당 contributor의 health check 수행 시간에 따라서 로그를 기록한다.

         

        Actuator contributor health check 수행 시간 로그 기록

        sprint boot actuator에서 연결된 각 contributor의 health check 수행 시간을 로그에 기록하기 위한 조건은 아래와 같다.

        • 로그 레벨은 WARN 이상 지정
        • contributor의 health check 수행 시간이 slow-indicator-logging-threshold 시간을 초과

        properties 설정은 아래와 같다.

        management:
          endpoint:
            health:
              logging:
                slow-indicator-threshold: 1ms
                
        logging:
          level:
            org.springframework.boot.actuate: warn

        contributor의 health check 소요 시간이 1ms 이상 소요되면 해당 contributor의 health check 소요 시간이 로그에 기록되도록 설정하였다. 

        위와 같이 설정 후 /actuator/health 를 호출하면 아래와 같이 로그가 생성 됨을 확인할 수 있다.

        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.cloud.health.RefreshScopeHealthIndicator (refreshScope) took 1ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.data.redis.RedisHealthIndicator (redis) took 219ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.cloud.stream.binder.kafka.KafkaBinderHealthIndicator (binders/kafka) took 8ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator (db) took 18ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.jms.JmsHealthIndicator (jms) took 107ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.mail.MailHealthIndicator (mail) took 281ms to respond

         

        Actuator contributor health check 대상에서 제외하기

        actuator contributor health check 대상 에서 제외하는 설정은 아래와 같다.

        management:
          health:
            jms:
              enabled: false
          endpoint:
            health:
              logging:
                slow-indicator-threshold: 1ns

        management.health.<name>.enabled=false 로 설정하면 해당 <name>에 해당하는 서비스의 health check는 수행하지 않는다.

        <name>에 해당 하는 목록은 spring actuator 문서에서 확인 가능하다.

        대부분의 contributor health check 소요 시간이 로그에 기록 되도록 slow-indicator-threshold 설정은 1ns(nano second)로 설정하였다.

        위와 같이 설정 후 /actuator/health 를 호출하면 아래와 같이 로그가 생성 됨을 확인 할 수 있다.

        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.cloud.health.RefreshScopeHealthIndicator (refreshScope) took 1ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.data.redis.RedisHealthIndicator (redis) took 203ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.cloud.stream.binder.kafka.KafkaBinderHealthIndicator (binders/kafka) took 21ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.system.DiskSpaceHealthIndicator (diskSpace) took 0ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.health.PingHealthIndicator (ping) took 0ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator (db) took 19ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.boot.actuate.mail.MailHealthIndicator (mail) took 298ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthContributor$$Lambda$3495/0x00000008014898a0 (discoveryComposite/discoveryClient) took 0ms to respond
        org.springframework.boot.actuate.health.HealthEndpointSupport | Health contributor org.springframework.cloud.config.client.ConfigServerHealthIndicator (clientConfigServer) took 0ms to respond

        위 로그를 보면 jms contributor의 health check는 로그에 기록되지 않아 health check에서 제외 되었 음을 알 수 있다.

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

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

        spring boot JMS activemq connection factory  (0) 2023.09.28
        Spring Boot AutoConfiguration 간단 모듈 만들어보기  (0) 2023.09.24
        spring boot websocket (웹소켓)  (0) 2023.09.20
        spring boot application startup tracking  (0) 2023.09.17
        Spring boot embedded server 의 default 포트 변경  (0) 2023.09.16
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바