複数のスレッド (それぞれが独自のアプリケーション コンテキストを持つ) を実行し、正常にシャットダウンする

ハムキンズ

SpringBoot 1.5 で再設計しているマルチスレッド アプリがあります。次の例を見てください。

@Service
@Lazy
class MyService {
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);

    private String account;

    private boolean stopped = false;
    private boolean processing;

    public MyService(String account) {
        logger.debug("MyService constructor");
        this.account = account;
    }

    public void run() {
        logger.debug("starting thread " + account);        
        while(!stopped) {
            try {
                processing = false;
                Thread.sleep(5000); // awaiting some service response
                processing = true;
                Thread.sleep(3000); // processing service response
            } catch (InterruptedException e) {
                logger.error(null,e);
            }
        }
        logger.debug("finished gracefully");
    }

    public void stop() {
        stopped = true;
    }
}

@SpringBootApplication
public class App {

    private static final String[] accounts = { "user1", "user2", "user3" };

    public static void main(String[] args) {
        for(String account : accounts) {
            new Thread(() -> {
                ConfigurableApplicationContext context = SpringApplication.run(App.class, account);
                BeanFactory factory = context.getBeanFactory();
                MyService service = factory.getBean(MyService.class, account);

                context.addApplicationListener(event -> {
                    if(event instanceof ContextClosedEvent) {
                        service.stop();
                        // context.registerShutdownHook();
                        // context.close();
                    }
                });
                service.run();
            }).start();
        }
    }
}

アプリケーション.プロパティ

logging.level.com.example = DEBUG

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>multicontext-app</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

</project>

スレッド固有のデータを持つ「シングルトン」スコープの Bean をオートワイヤーしたいので、マルチコンテキスト構成を思い付きました。

質問:

  1. スレッドごとにアプリケーションコンテキストを作成するのは正しい方法ですか?
  2. 重複したログ メッセージ (スレッド数の二乗) が表示されるのはなぜですか? たとえば、「MyService コンストラクター」メッセージは、3 つのスレッドが実行されているときに 3 回 (コンテキストごとに 1 つのインスタンス) ではなく、9 回出力されます。
  3. サービスが応答を待っていて処理していない場合に待つ必要がないことを考慮して、各サービス スレッドを適切にシャットダウンする方法は? 現在、アプリが停止したときに「正常に終了しました」というメッセージが表示されません。
  4. 電話する必要がありますcontext.close()か、context.registerShutdownHook()またはその両方が必要ですか? いつそれを行う必要があり、それをしないと何が起こるでしょうか?
vanOekel

正常なシャットダウンを実現するにはさまざまな方法があります。ここでは、スケジュール済みのトリック (StackOverflow からどこかで学んだ方法です) を使用してその方法を示します。

スケジュールされたトリックは、アプリケーション コンテキストの開始時にタスクを開始しますが、タスクは再びスケジュールされることはなく ( initialDelay = 0L, fixedDelay = Long.MAX_VALUE)、スケジュールされたタスクを効果的にバックグラウンド サービスに変えます。Spring はタスクをスケジュールしSchedulingConfigurer、シャットダウンを含むスケジュールされたタスクを制御できる を介してスケジュールされたタスクを処理する方法を Spring に設定できます

実行中のタスクを停止する通常の方法は、それらを中断することであるため、それを使用してサービスを停止しました。ただし、ContextClosedEvent本当に必要な場合は使用してサービスの実行を停止できます。

スケジュールされたタスクは非同期で開始されるため、メイン メソッドの別のスレッドでアプリケーション コンテキストを開始する必要はありません。

:私は使用してコマンドライン上でテスト
mvn clean spring-boot:run
し、
mvn clean package spring-boot:repackage
java -jar target\[app].jar
シャットダウンを開始するために押して、「CTRL-C」。

@SpringBootApplication
@EnableScheduling
public class App implements SchedulingConfigurer {

    private static final Logger log = LoggerFactory.getLogger(App.class);
    private static final AtomicInteger ctxCount = new AtomicInteger();

    private static final String[] accounts = { "user1", "user2", "user3" };

    public static void main(String[] args) {

        Arrays.stream(accounts).forEach(account -> {
            ConfigurableApplicationContext context = SpringApplication.run(App.class, account);
            context.getBeanFactory().getBean(MyService.class, account);
        });
    }

    @Bean(destroyMethod="shutdown")
    public Executor taskScheduler() {
        // https://github.com/spring-projects/spring-boot/issues/7779
        final String prefix = "app-" + ctxCount.incrementAndGet() + "-mcsch-";
        log.debug("Creating task scheduler {}", prefix);
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(1);
        scheduler.setThreadNamePrefix(prefix);
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        scheduler.setAwaitTerminationSeconds(20);
        return scheduler;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        log.debug("Setting task scheduler.");
        taskRegistrar.setScheduler(taskScheduler());
    }

    @Service
    @Lazy
    static class MyService {

        private String account;

        public MyService(String account) {
            log.debug("{} - MyService constructor", account);
            this.account = account;
        }

        // trick to "run at startup"
        @Scheduled(initialDelay = 0L, fixedDelay = Long.MAX_VALUE)
        public void run() {
            boolean stopped = false;
            while(!stopped) {
                try {
                    log.debug("{} - sleeping", account);        
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    log.debug("{} - sleep interrupted", account);
                    stopped = true;
                }
            }
            log.debug("{} - finished gracefully", account);
        }
    } // MyService

}

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ