web-dev-qa-db-ja.com

Eclipseで終了したSpring Bootアプリ-シャットダウンフックが呼び出されない

Spring Boot + Spring Data Redis/KeyValueプロジェクトがあります。すべての依存関係が埋め込まれた状態でアプリケーションを実行するようにSpringプロファイルをセットアップしました。そこで、起動時に組み込みRedisサーバーを起動します。 Spring Bootアプリケーションを停止したときにRedisサーバーを停止したい場合を除いて、Eclipseで起動するとすべてが正常に機能します。そのため、いくつかのシャットダウンフックを設定しましたが、Eclipseからアプリケーションを終了するときに呼び出されません。

それらはSOに関する同様の質問ですRedisソリューションがあることを期待してこれを作成しました。また、これらの同様の質問は、Spring Bootに固有のものではありません。

私は多くのことを試しました:

  • スプリングブーツExitCodeGenerator;
  • DisposableBean;
  • @PreDestroy;
  • ShutdownHookを試しました(@ mp911deの回答の後)

それらのどれも呼び出されません。

おそらく、Redisオプション、タイムアウト、キープアライブなどがあります。ボックスの外にある何か気づいていませんか? Spring Bootアプリが突然停止したときに、Redisサーバーが停止していることをどのように確認できますか

=>私はこれを見た 春のブーツ用の埋め込みRedis だが@PreDestroyは、アプリケーションを突然終了したときに呼び出されません。

同様の質問のいくつかを次に示します。

また、Eclipse.orgでこの投稿を見て、Eclipseからアプリケーションを停止するときにシャットダウンフックが呼び出されない方法について説明しました: Graceful shutdown of Java Applications


ここに私のすべての関連コードがあります:

起動するコンポーネント組み込み起動時のRedisサーバー(停止しようとしても!!):

@Component
public class EmbeddedRedis implements ExitCodeGenerator, DisposableBean{

    @Value("${spring.redis.port}")
    private int redisPort;

    private RedisServer redisServer;

    @PostConstruct
    public void startRedis() throws IOException {
        redisServer = new RedisServer(redisPort);
        redisServer.stop();
        redisServer.start();
    }

    @PreDestroy
    public void stopRedis() {
        redisServer.stop();
    }

    @Override
    public int getExitCode() {
        redisServer.stop();
        return 0;
    }

    @Override
    public void destroy() throws Exception {
        redisServer.stop();
    }
}

application.properties:

spring.redis.port=6379

Spring Boot App:

@SpringBootApplication
@EnableRedisRepositories
public class Launcher {

    public static void main(String[] args){
        new SpringApplicationBuilder() //
        .sources(Launcher.class)//
        .run(args);
    }

    @Bean
    public RedisTemplate<String, Model> redisTemplate() {
        RedisTemplate<String, Model> redisTemplate = new RedisTemplate<String, Model>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        return redisTemplate;
    }


    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName("localhost");
        return jedisConnectionFactory;
    }
}

Redisサーバー(〜埋め込み)実行中:

$ ps -aef | grep redis
 ... /var/folders/qg/../T/1472402658070-0/redis-server-2.8.19.app *:6379  

埋め込まれたRedis maven依存関係:

<dependency>
    <groupId>com.github.kstyrc</groupId>
    <artifactId>embedded-redis</artifactId>
    <version>0.6</version>
</dependency>
8
alexbt

SpringBootとEclipseを使用する場合、 [〜#〜] sts [〜#〜] (Spring Tool Suite)Eclipseプラグインをインストールして、正常なシャットダウンを実現できます。

インストールしたら、通常の「Javaアプリケーション」ではなく「Spring Boot App」としてアプリケーションを実行します(実行/デバッグ構成)

「ライフサイクル管理を有効にする」チェックボックスがオンになっていることを確認します。赤い四角ボタンをクリックしてアプリケーションを停止すると、強制終了の代わりに正常なシャットダウンが実行されます。

編集:

「赤い四角ボタン」が2つあることに注意してください。 1つは[起動]ツールバーにあり、もう1つは[コンソール]パネルにあります。起動ツールバーの1つはハードキルを実行しますが、コンソールの1つはスプリングブートアプリケーションの正常なシャットダウンを可能にします(STSで起動)

13
Ghurdyl

調査の結果、Eclipseはアプリケーションを終了するだけで、シャットダウンフックが実行される機会がないことがわかりました。

_Spring-Boot_の配線方法のおかげで、回避策/ハックを見つけました。 mainメソッドが実行されると、Tomcatは別のスレッドで開始され、mainメソッドは完了するまで実行を続けます。これにより、Enterキーを押したときに終了ロジックを挿入できました。

アプリケーションは正常に起動し、コンソールはEnterキーがSystem.exit(1);を実行するのを待つだけです。

_@SpringBootApplication
@EnableRedisRepositories
public class Launcher {
    public static void main(String[] args){
        new SpringApplicationBuilder() //
        .sources(Launcher.class)//
        .run(args);

        System.out.println("Press 'Enter' to terminate");
        new Scanner(System.in).nextLine();
        System.out.println("Exiting");
        System.exit(1);
    }
}
_

Eclipseからアプリケーションを起動するとき、インターフェースからアプリケーションを終了する代わりに、コンソールでEnterキーを押します。これで、シャットダウンフック(_@PreDestroy_)がトリガーされ、Redisサーバーが停止します。

私が望んでいたことではありませんが、少なくとも当面は、Embedded Redis Serverがアプリケーションで停止され、手動で強制終了する必要はありません。

5
alexbt

ExitCodeGeneratorは、アプリケーションがexitメソッドを呼び出す必要があります

System.exit(SpringApplication
             .exit(SpringApplication.run(SampleBatchApplication.class, args)));

さらに シャットダウンフック を登録できます。フックが呼び出されます

  • VMが正常に終了したとき(System.exit)。
  • 割り込み(Ctrl+CSIGINT)またはシグナル(SIGHUPSIGTERM)。

状況によっては、VMが正常にシャットダウンしない場合(SIGKILL、internal VMエラー、ネイティブメソッドのエラー))、シャットダウンフックが呼び出されるかどうかを保証します。

EmbeddedRedisコンポーネントのコードは次のようになります。

@PostConstruct
public void startRedis() throws IOException {

    redisServer = new RedisServer(redisPort);
    redisServer.start();

    Runtime.getRuntime().addShutdownHook(new Thread(){

        @Override
        public void run() {
            redisServer.stop();
        }
    });
}
3
mp911de

Redisではなく、同じ問題がありました。他の開発者が望まない場合にSTSを追加する必要がないように、私は最大限の移植性を望みました。これを任意のSpring Bootアプリケーションに追加して、クリーンシャットダウンを提供できます。 OP自身の答えを詳しく説明したのは、ここにあります。

これをメインクラスまたは@Configurationクラスに追加します。

    @Bean
    public ApplicationRunner systemExitListener() {
        return args -> {
            if (args.getOptionValues("exitListener") != null) {
                System.out.println("Press Enter to exit application");
                new Scanner(System.in).nextLine();
                System.out.println("Exiting");
                System.exit(0);
            }
        };
    }

次に、Eclipse(または、選択したIDE =選択、またはコマンドラインで)で、引数--exitListenerを追加してコードをアクティブにします。Eclipseでは、実行構成にあります、「引数」タブの「プログラムの引数」ボックス。

1
Ben Ingle