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> 
내가 autowire하기 위해 "싱글"을 원하기 때문에 내가 여러 컨텍스트 구성 해낸

스레드 특정 데이터가있는 범위가 지정된 bean.

질문 :이 스레드 당 애플리케이션 컨텍스트를 생성 할 수있는 올바른 방법

  1. 인가?
  2. 중복 된 로그 메시지 (스레드 번호 제곱 시간)가 표시되는 이유는 무엇입니까? 예를 들어, 「MyService constructor」메세지는, 3 개의 thread가 실행 중일 때, 3 회 (문맥 당 1 개의 인스턴스) 대신에, 9 회 인쇄됩니다.
  3. 서비스가 응답을 기다리고 처리하지 않을 때까지 기다릴 필요가 없다는 점을 고려하여 각 서비스 스레드를 정상적으로 종료하는 방법은 무엇입니까? 현재 앱이 중지되었을 때 '정상적으로 완료되었습니다'라는 메시지가 표시되지 않습니다.
  4. context.close() 또는 context.registerShutdownHook() 또는 둘 모두를 호출해야합니까? 그 일을해야 할 때 무엇이 ​​일어날 것인가?
+0

궁금한 점이 있으십니까? 상태 풀 싱글톤 빈의 사용 사례는 무엇입니까? 이것은 내가 전에 봄 부츠에서 만나지 못한 패턴입니다. 단지 호기심이 생깁니다. –

+0

평생 동안 인증 토큰을 유지하는 HttpClient는 어떻습니까? 아니면 여러 번 인증하는 것이 더 좋을까요? 빈의 인스턴스만큼 생성됩니까? – gumkins

답변

1

정상적인 종료를 얻는 데는 여러 가지 방법이 있지만, 나는 (StackOverflow 어딘가에서 배운) Scheduled 트릭을 사용하여 보여줄 것입니다.

예약 된 트릭은 응용 프로그램 컨텍스트 시작시 작업을 시작하지만 작업을 다시 예약하지 않습니다 (initialDelay = 0L, fixedDelay = Long.MAX_VALUE). 예약 된 작업을 효과적으로 백그라운드 서비스로 전환합니다. Spring은 작업을 예약하고, 예약 된 작업을 처리하는 방법을 SchedulingConfigurer을 통해 구성 할 수 있습니다.이 작업을 통해 종료를 포함하여 예약 된 작업을 제어 할 수 있습니다.

실행중인 작업을 중지하는 일반적인 방법은 서비스를 중단하는 것이므로 서비스를 중지하는 데 사용했습니다. 그러나 실제로 필요한 경우 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 

}