Eureka 환경에서 client들이 Registry 를 local cache에 저장하는 이유로 무중단 배포에 차질이 있었다. 이를 해결하기 위한 여러 방법을 모색하였고, 그 중 하나가 GracefulShutdown 을 이용하는 것이었다. 하지만 기존 Spring Web - Tomcat 에서의 GracefulShutdown 관련 레퍼런스가 많은 것에 비해 Netty 에서의 GracefulShutdown 의 레퍼런스는 거의 없었다. 이에 대해 조사하다보니 Netty 기본 개념에 대해서도 조사하고 이 문서를 이해하기 위한 내용을 여기 (Netty 기본 개념) 에 정리했다
거두절미 하고 참고한 사이트와 관련 코드는 다음과 같다
https://blog.fearcat.in/a?ID=00001-36c2265c-c4a8-4dde-ac9c-74fc87b0c532
https://www.infoq.cn/article/netty-elegant-exit-mechanism-and-principles
@Component
public class GracefulShutdown {
@Autowired
ReactorResourceFactory reactorResourceFactory;
LoopResources loopResources;
//SpringBoot 2.1.5 reactor.netty.resources.LoopResources#dispose subscribe block
@PostConstruct
void init() throws Exception {
Field field = TcpResources.class.getDeclaredField("defaultLoops");
field.setAccessible(true);
loopResources = (LoopResources) field.get(reactorResourceFactory.getLoopResources());
field.setAccessible(false);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Graceful: block long to 20s before real shutdown!");
loopResources.disposeLater().block(Duration.ofSeconds(20));
}));
}
}
코드를 하나하나 분석해보자
Field field = TcpResources.class.getDeclaredField("defaultLoops");
field.setAccessible(true);
loopResources = (LoopResources) field.get(reactorResourceFactory.getLoopResources());
field.setAccessible(false);
여기서는 defaultLoop 를 가져와서 잠시 접근가능하게 하고, reactorResourceFactory 를 통해 defaultLoop 의 LoopResource 를 가져온다. 그 후 다시 접근 불가능하게 설정해주었다.
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Graceful: block long to 20s before real shutdown!");
loopResources.disposeLater().block(Duration.ofSeconds(20));
}));
이 코드는 다음과 같이 변환 될 수 있다.
Thread t = new Thread(() -> {
System.out.println("Graceful: block long to 20s before real shutdown!");
loopResources.disposeLater().block(Duration.ofSeconds(20));
});
Runtime.getRuntime().addShutdownHook(t);
즉, Shutdown Process 를 진행하는 Thread instance 를 생성 후에, 이를 런타임에 ShutdownHook 으로 걸어준다
그리고, 그 Thread 는 LoopResource 의 disposeLater() 함수를 호출하는데, 이미 진행되고 있는 구독이 취소되지 않게 하기위해 DEFAULT_SHUTDOWN_QUIET_PERIOD(2초) 후에 취소되게 한다
정리하자면 Graceful Shutdown Process 에 어떤 Logic 을 추가하고 싶다면 addShutdownHook 에 걸어주는 Thread 내에 로직을 추가하면 된다!!