2017-04-10 37 views
1

我有一個多線程應用程序,我正在使用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(); 
     } 
    } 
} 

application.properties

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. 爲什麼我可以看到重複的日誌消息(線程數平方時間)?例如,當3個線程正在運行時,「MyService構造函數」消息被打印9次,而不是3次(每個上下文有一個實例)。
  3. 如何正常關閉每個服務線程,考慮到如果服務正在等待響應而不處理它,則不需要等待?目前,當應用程序停止時,我看不到「完成優雅」的消息。
  4. 我需要撥打context.close()context.registerShutdownHook()或兩者嗎?當我應該這樣做時,會發生什麼事情,我不會那樣做?
+0

我想知道,stateful ful單身豆的用例是什麼?這是我之前從未在Spring引導中遇到過的模式,只是好奇而已。 –

+0

HttpClient保持身份驗證令牌有一些生命週期?或者多次驗證會更好?多少個bean的實例被創建? – gumkins

回答

1

有不同的方法來實現正常關機,我將通過使用計劃技巧(我從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 

} 
相關問題