Spring Boot 최신 판 우아 한 정지 방법 을 자세히 알 아 보 세 요.

우아 한 정지 란 무엇 입 니까?
먼저 간단 한 코드 를 주세요.다음 과 같 습 니 다.

@RestController
public class DemoController {
 @GetMapping("/demo")
 public String demo() throws InterruptedException {
 //           
 Thread.sleep(20 * 1000L);
 return "hello";
 }
}
데이터 가 이 인터페이스 에서 업무 논 리 를 실행 하 라 고 요청 할 때 서버 에서 전원 이 꺼 져 있 으 면 spring boot 의 기본 상황 은 용기(tomcat 등)를 직접 닫 아서 이 업무 논리 실행 에 실 패 했 습 니 다.일부 업무 장면 에서 데이터 가 일치 하지 않 는 상황 이 발생 하고 사무 논 리 는 스크롤 백 되 지 않 습 니 다.

오픈 소스 항목:
분산 식 모니터링(Gitee GVP 의 가장 가치 있 는 오픈 소스 프로젝트):https://gitee.com/sanjiankethree/cubic
카메라 영상 흐름 채집:https://gitee.com/sanjiankethree/cubic-video
우아 한 정지
현재 Spring Boot 는 2.3.4.RELEASE 까지 발 전 했 고 2.3 버 전이 도래 함 에 따라 우아 한 정지 체제 도 더욱 완선 되 었 다.
현재 버 전의 Spring Boot 우아 한 정지 지원 Jetty,Reactor Netty,Tomcat,Undertow 및 반응 식 및 Servlet 기반 웹 프로그램 은 우아 한 정지 기능 을 지원 합 니 다.
우아 한 정지 의 목적:
우아 한 정지 가 없 으 면 서버 가 직접 닫 습 니 다(kill-9).그러면 현재 용기 에서 실행 중인 업무 가 직접 실패 하고 특정한 특수 한 장면 에서 더러 운 데 이 터 를 만 들 수 있 습 니 다.
우아 한 정지 설정 이 추 가 된 후:
서버 가 종료(kill-2)를 실행 할 때 용기 내부 업무 스 레 드 가 실 행 될 시간 을 조금 남 깁 니 다.이 때 용기 도 새로운 요청 이 들 어 갈 수 없습니다.새로운 요청 의 처리 방식 은 웹 서버 와 관련 이 있 으 며,Reactor Netty,Tomcat 은 접속 요청 을 중단 합 니 다.Undertow 의 처리 방식 은 503 으로 되 돌아 갑 니 다.
새 버 전 설정
YAML 설정
새 버 전 설정 은 매우 간단 합 니 다.server.shutdown=graceful 이면 됩 니 다.(주의,우아 한 정지 설정 은 Tomcat 9.0.33(포함)이상 버 전에 맞 춰 야 합 니 다.)

server:
 port: 6080
 shutdown: graceful #      
spring:
 lifecycle:
 timeout-per-shutdown-phase: 20s #         30s
버퍼 파라미터 timeout-per-shutdown-phase 를 설정 한 후 정 해진 시간 내 에 스 레 드 가 실행 되 지 않 으 면 강제로 정지 합 니 다.
다음은 정지 할 때 우아 한 정지 로그 와 추가 하지 않 는 차 이 를 살 펴 보 겠 습 니 다.

//        
Disconnected from the target VM, address: '127.0.0.1:49754', transport: 'socket'
Process finished with exit code 130 (interrupted by signal 2: SIGINT)
우아 한 정지 설정 을 추가 하면 로그 Waiting for active requests to cpmplete 를 뚜렷하게 발견 할 수 있 습 니 다.이 때 용 기 는 Shutdown Hook 이 실 행 된 후에 중 단 됩 니 다.

닫 기 방식
1.kill-9 동작 을 사용 하지 말고 kill-2 를 사용 하여 용 기 를 닫 습 니 다.이렇게 해야만 자바 내부 Shutdown Hook 작업 을 촉발 할 수 있 습 니 다.kill-9 는 Shutdown Hook 을 촉발 하지 않 습 니 다.
2.터미널 모니터링 POST 요청/actuator/shutdown 을 사용 하여 우아 하 게 전원 을 끌 수 있 습 니 다.
ShutdownHook 추가
위의 로 그 를 통 해 우 리 는 Druid 가 자신의 Shutdown Hook 을 실행 한 것 을 발 견 했 습 니 다.그러면 우 리 는 Shutdown Hook 을 추가 합 니 다.몇 가지 간단 한 방법 이 있 습 니 다.
1.DisposableBean 인 터 페 이 스 를 실현 하고 destroy 방법 실현

@Slf4j
@Service
public class DefaultDataStore implements DisposableBean {


 private final ExecutorService executorService = new ThreadPoolExecutor(OSUtil.getAvailableProcessors(), OSUtil.getAvailableProcessors() + 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(200), new DefaultThreadFactory("UploadVideo"));


 @Override
 public void destroy() throws Exception {
 log.info("           DisposableBean");
 executorService.shutdown();
 }
}
2,사용@PreDestroy 주해

@Slf4j
@Service
public class DefaultDataStore {


 private final ExecutorService executorService = new ThreadPoolExecutor(OSUtil.getAvailableProcessors(), OSUtil.getAvailableProcessors() + 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(200), new DefaultThreadFactory("UploadVideo"));


 @PreDestroy
 public void shutdown() {
 log.info("         @PreDestroy");
 executorService.shutdown();
 }

}
여기 주의,@PreDestroy 가 DisposableBean 보다 먼저 실 행 됩 니 다.
폐쇄 원리
1、kill pid 를 사용 하여 닫 습 니 다.원본 코드 는 간단 합 니 다.GracefulShutdown 을 보 실 수 있 습 니 다.

	private void doShutdown(GracefulShutdownCallback callback) {
		List<Connector> connectors = getConnectors();
		connectors.forEach(this::close);
		try {
			for (Container host : this.tomcat.getEngine().findChildren()) {
				for (Container context : host.findChildren()) {
					while (isActive(context)) {
						if (this.aborted) {
							logger.info("Graceful shutdown aborted with one or more requests still active");
							callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
							return;
						}
						Thread.sleep(50);
					}
				}
			}

		}
		catch (InterruptedException ex) {
			Thread.currentThread().interrupt();
		}
		logger.info("Graceful shutdown complete");
		callback.shutdownComplete(GracefulShutdownResult.IDLE);
	}
2.터미널 모니터링 POST 요청/actuator/shutdown 종료 사용
actuator 는 모두 SPI 의 확장 방식 을 사 용 했 기 때문에 AutoConfiguration 을 살 펴 보면 관건 은 Shutdown Endpoint 입 니 다.

@Configuration(
 proxyBeanMethods = false
)
@ConditionalOnAvailableEndpoint(
 endpoint = ShutdownEndpoint.class
)
public class ShutdownEndpointAutoConfiguration {
 public ShutdownEndpointAutoConfiguration() {
 }

 @Bean(
 destroyMethod = ""
 )
 @ConditionalOnMissingBean
 public ShutdownEndpoint shutdownEndpoint() {
 return new ShutdownEndpoint();
 }
}
Shutdown Endpoint,편폭 을 절약 하기 위해 중요 한 것 만 남 겼 습 니 다.

@Endpoint(
 id = "shutdown",
 enableByDefault = false
)
public class ShutdownEndpoint implements ApplicationContextAware {
 
 @WriteOperation
 public Map<String, String> shutdown() {
 if (this.context == null) {
  return NO_CONTEXT_MESSAGE;
 } else {
  boolean var6 = false;

  Map var1;
  try {
  var6 = true;
  var1 = SHUTDOWN_MESSAGE;
  var6 = false;
  } finally {
  if (var6) {
   Thread thread = new Thread(this::performShutdown);
   thread.setContextClassLoader(this.getClass().getClassLoader());
   thread.start();
  }
  }

  Thread thread = new Thread(this::performShutdown);
  thread.setContextClassLoader(this.getClass().getClassLoader());
  thread.start();
  return var1;
 }
 }
 
 private void performShutdown() {
 try {
  Thread.sleep(500L);
 } catch (InterruptedException var2) {
  Thread.currentThread().interrupt();
 }

 this.context.close(); //      
 }
}
this.context.close()를 호출 했 습 니 다.사실은 AbstractApplication Context 의 close()방법 입 니 다.(중점 은 doClose()입 니 다.)

/**
	 * Close this application context, destroying all beans in its bean factory.
	 * <p>Delegates to {@code doClose()} for the actual closing procedure.
	 * Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
	 * @see #doClose()
	 * @see #registerShutdownHook()
	 */
	@Override
	public void close() {
		synchronized (this.startupShutdownMonitor) {
			doClose(); //  :  bean    jvm shutdown hook
			// If we registered a JVM shutdown hook, we don't need it anymore now:
			// We've already explicitly closed the context.
			if (this.shutdownHook != null) {
				try {
					Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
				}
				catch (IllegalStateException ex) {
					// ignore - VM is already shutting down
				}
			}
		}
	}
후기
여기까지 버 전의 Spring Boot 우아 정지 에 대해 서 는 말 이 끝났다.왜 라 고 했 어 요?닫 을 때 서버 내부 스 레 드 의 실행 이 완료 되 었 을 뿐 호출 자의 상 태 는 관심 이 없다 는 것 을 알 수 있 기 때문이다.
Dubbo 든 Cloud 의 분포 식 서비스 프레임 워 크 든 서비스 가 중단 되 기 전에 공급 자 를 등록 센터 에서 반 등록 한 다음 에 서비스 제공 자 를 정지 시 켜 야 업무 시스템 이 각종 503,timeout 등 현상 이 발생 하지 않도록 확보 할 수 있다.
다행히 현재 Spring Boot 는 Kubernetes 와 결합 하여 이 점 을 해결 해 주 었 습 니 다.즉,Spring Boot 2.3 버 전의 새로운 기능 인 Liveness(생존 상태)와 Readiness(준비 상태)입 니 다.
간단하게 이 두 상 태 를 제시 합 니 다.
  • 라 이브 네 스(생존 상태):라 이브 네 스 상 태 를 살 펴 보면 내부 상황 을 헬 스 체크 로 이해 할 수 있 습 니 다.라 이브 네 스 가 실패 하면 응용 프로그램 이 고장 상태 에 있 고 현재 회복 이 불가능 하 다 는 것 을 의미 합 니 다.이 경우 다시 시작 하 세 요.이때 Kubernetes 는 생존 탐지 에 실패 하면 Container 를 죽 입 니 다.
  • Readiness(준비 상태):응용 프로그램 이 클 라 이언 트 요청 을 받 아들 일 준비 가 되 어 있 는 지 알려 줍 니 다.만약 에 Readiness 가 준비 되 지 않 으 면 k8s 는 데이터 로 올 수 없습니다.
  • 스프링 부 트 최신 판 우아 정지 에 관 한 글 은 여기까지 입 니 다.스프링 부 트 우아 정지 에 관 한 더 많은 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기