programing

프로그래밍 방식으로 스프링 부트 응용 프로그램 다시 시작 / 스프링 컨텍스트 새로 고침

muds 2023. 9. 14. 00:03
반응형

프로그래밍 방식으로 스프링 부트 응용 프로그램 다시 시작 / 스프링 컨텍스트 새로 고침

사용자가 개입하지 않고 프로그래밍 방식으로 Spring Application을 재시작하려고 합니다.

기본적으로 애플리케이션의 모드를 전환할 수 있는 페이지가 있으며(실제로는 현재 활성화된 프로파일을 전환하는 것을 의미합니다) 제가 이해하는 한 컨텍스트를 다시 시작해야 합니다.

현재 내 코드는 매우 간단합니다. 단지 재시작 비트를 위한 것입니다(그런데 이것은 Kotlin입니다).

    context.close()
    application.setEnvironment(context.environment)
    ClassUtils.overrideThreadContextClassLoader(application.javaClass.classLoader)
    context = application.run(*argsArray)

하지만 내가 하는 순간은context.close()JVM이 즉시 존재합니다.저도 노력했습니다.context.refresh()하지만 그것은 단순히 톰캣/제티를 죽이는 것처럼 보입니다. (톰캣 문제일 경우 둘 다 시도해 보았지만) 그리고 아무 일도 일어나지 않습니다.

또한 프로그래밍 방식으로 Spring Boot 애플리케이션을 재시작하는 을 보았지만, 그 답변들을 보면 아무것도 효과가 없는 것 같습니다.또한 스프링 액츄에이터에 대해 조사했는데, 스프링 액츄에이터는 다음과 같습니다./restart엔드포인트는 없어 보이는데요?

알렉스의 솔루션이 효과가 있지만, 2개의 추가 의존성을 포함하는 것을 믿지 않습니다 (Actuator그리고.Cloud Context한 번의 수술을 할 수 있게 된 것입니다.대신 그의 답변을 종합하고 내가 원하는 것을 하기 위해 코드를 수정했습니다.

따라서 우선, 코드를 실행하는 이 중요합니다.new Thread()그리고.setDaemon(false);. 재시작을 처리하는 다음과 같은 Endpoint 메서드가 있습니다.

val restartThread = Thread {
    logger.info("Restarting...")
    Thread.sleep(1000)
    SpringMain.restartToMode(AppMode.valueOf(change.newMode.toUpperCase()))
    logger.info("Restarting... Done.")
}
restartThread.isDaemon = false
restartThread.start()

Thread.sleep(1000)필요는 없지만 애플리케이션을 실제로 다시 시작하기 전에 컨트롤러에서 보기를 출력하기를 원합니다.

SpringMain.restartToMode는 다음과 같습니다.

@Synchronized fun restartToMode(mode: AppMode) {
    requireNotNull(context)
    requireNotNull(application)

    // internal logic to potentially produce a new arguments array

    // close previous context
    context.close()

    // and build new one using the new mode
    val builder = SpringApplicationBuilder(SpringMain::class.java)
    application = builder.application()
    context = builder.build().run(*argsArray)
}

어디에context그리고.application출신.main응용 프로그램을 시작할 때 메소드:

val args = ArrayList<String>()
lateinit var context: ConfigurableApplicationContext
lateinit var application: SpringApplication

@Throws(Exception::class)
@JvmStatic fun main(args: Array<String>) {
    this.args += args

    val builder = SpringApplicationBuilder(SpringMain::class.java)
    application = builder.application()
    context = builder.build().run(*args)
}

저는 이것이 문제를 일으키는지 완전히 확신할 수 없습니다.만약 있다면 이 답변을 업데이트하겠습니다.이것이 다른 사람들에게 조금이라도 도움이 되기를 바랍니다.

만약 누군가에게 도움이 될지도 모르니, 여기 크림보의 대답을 pura Java로 번역한 것이 있습니다.

컨트롤러 방식:

@GetMapping("/restart")
void restart() {
    Thread restartThread = new Thread(() -> {
        try {
            Thread.sleep(1000);
            Main.restart();
        } catch (InterruptedException ignored) {
        }
    });
    restartThread.setDaemon(false);
    restartThread.start();
}

주 클래스(유의 비트만 해당):

private static String[] args;
private static ConfigurableApplicationContext context;

public static void main(String[] args) {
    Main.args = args;
    Main.context = SpringApplication.run(Main.class, args);
}

public static void restart() {
    // close previous context
    context.close();

    // and build new one
    Main.context = SpringApplication.run(Main.class, args);

}

이미 언급했듯이, 스레드를 통한 재시작 구현은 한 번만 작동하고 두 번째는 컨텍스트가 null이기 때문에 NPE를 버립니다.

재시작 스레드가 초기 메인 호출 스레드와 동일한 클래스 로더를 사용하도록 함으로써 이 NPE를 방지할 수 있습니다.

private static volatile ConfigurableApplicationContext context;
private static ClassLoader mainThreadClassLoader;

public static void main(String[] args) {
    mainThreadClassLoader = Thread.currentThread().getContextClassLoader();
    context = SpringApplication.run(Application.class, args);
}

public static void restart() {
    ApplicationArguments args = context.getBean(ApplicationArguments.class);

    Thread thread = new Thread(() -> {
        context.close();
        context = SpringApplication.run(Application.class, args.getSourceArgs());
    });

    thread.setContextClassLoader(mainThreadClassLoader);
    thread.setDaemon(false);
    thread.start();
}

저는 스프링 데브툴즈의 리스타터를 사용하여 이 문제를 해결했습니다.pom.xml에 추가:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
</dependency>

그런 다음 org.springframework.boot.devtools.restart를 사용합니다.다시 시작하여 다음을 호출합니다.

Restarter.getInstance().restart();

저한테는 효과가 있어요.도움이 되길 바랍니다.

사용할 수 있습니다.RestartEndPoint(에서)spring-cloud-context종속성)을 사용하여 프로그래밍 방식으로 Spring Boot 응용 프로그램을 다시 시작합니다.

@Autowired
private RestartEndpoint restartEndpoint;

...

Thread restartThread = new Thread(() -> restartEndpoint.restart());
restartThread.setDaemon(false);
restartThread.start();

메모리 누수가 발생할 수 있음을 알려주는 예외가 발생하더라도 작동합니다.

[xyx] 웹 응용 프로그램이 [Thread-6]이라는 이름의 스레드를 시작했지만 중지하지 못했습니다.이로 인해 메모리 누수가 발생할 가능성이 매우 높습니다.스레드의 스택 추적:

이 다른 질문에 대해서도 동일한 답변이 제공되었습니다(다른 단어로 표시됨).자바 기능을 사용하여 스프링 부팅에서 스프링 액추에이터/재시동 엔드포인트 호출

아래의 재시작 방법이 작동합니다.

'@SpringBootApplication 공용 클래스 애플리케이션 {

private static ConfigurableApplicationContext context;

public static void main(String[] args) {
    context = SpringApplication.run(Application.class, args);
}

public static void restart() {
    ApplicationArguments args = context.getBean(ApplicationArguments.class);

    Thread thread = new Thread(() -> {
        context.close();
        context = SpringApplication.run(Application.class, args.getSourceArgs());
    });

    thread.setDaemon(false);
    thread.start();
}

}`

언급URL : https://stackoverflow.com/questions/39386168/programmatically-restart-spring-boot-application-refresh-spring-context

반응형