簡體   English   中英

我可以在使用 Spring 的計划任務中使用 CompletableFuture 嗎?

[英]Can I use CompletableFuture in Scheduling Tasks with Spring?

問題:
我的應用程序正在使用 Spring Boot 和 Java 8。我有一個計划任務來從三個不同的數據源獲取一些數據,但現在它們正在按順序運行。 我想通過同時使用 CompletableFuture 划分三個數據檢索操作來加速它們。 但是,我運行代碼並發現 CompletableFuture 任務在一個名為“scheduling-1”的單個線程中運行,而不是異步運行。

我試過的:
我以為我在做一些不太好的事情。 所以,我決定退后一步,通過運行一個小型測試項目來嘗試一下。 我嘗試過的就像我結合了以下兩個示例提到的技術:

  1. https://spring.io/guides/gs/scheduling-tasks/
  2. https://spring.io/guides/gs/async-method/

但是當我把它們放在一起時,我發現它們在調用 Async 方法時使用的是同一個線程。

output 是:

[   scheduling-1] Looking up PivotalSoftware, thread: scheduling-1  
[   scheduling-1] Looking up CloudFoundry, thread: scheduling-1  
[   scheduling-1] Looking up Spring-Projects, thread: scheduling-1

我的問題:
是我的代碼出錯了,還是調度程序中只有一個線程可用,即使我使用 Async for CompletableFuture? 我的實驗結果背后的根本原因是什么?

我的代碼:

異步方法應用程序

@SpringBootApplication  
@EnableAsync  
@EnableScheduling  
@PropertySource("classpath:common.properties")  
public class AsyncMethodApplication {

    public static void main(String[] args) {
        // close the application context to shut down the custom ExecutorService
        SpringApplication.run(AsyncMethodApplication.class, args).close();
    }

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("GithubLookup-");
        executor.initialize();
        return executor;
    }


}

我的服務

@Service  
public class MyService {  
    
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);
    
    @Autowired
    private UserLookupService gitHubLookupService;
    
    @Scheduled(cron = "${cron.expression.reloadUserData}")
    @PostConstruct
    public void loadCurrentDayUsers() throws Exception {
        
        this.loadUsers();
        // this.loadOthers();
        
    }
    
    public void loadUsers() throws Exception {
        
        // Start the clock
        long start = System.currentTimeMillis();

        // Kick of multiple, asynchronous lookups
        CompletableFuture<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
        CompletableFuture<User> page2 = gitHubLookupService.findUser("CloudFoundry");
        CompletableFuture<User> page3 = gitHubLookupService.findUser("Spring-Projects");

        // Wait until they are all done
        CompletableFuture.allOf(page1,page2,page3).join();

        // Print results, including elapsed time
        logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
        logger.info("--> " + page1.get());
        logger.info("--> " + page2.get());
        logger.info("--> " + page3.get());
    }

}

用戶

@JsonIgnoreProperties(ignoreUnknown=true)  
public class User {  

    private String name;
    private String blog;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBlog() {
        return blog;
    }

    public void setBlog(String blog) {
        this.blog = blog;
    }

    @Override
    public String toString() {
        return "User [name=" + name + ", blog=" + blog + "]";
    }

}

用戶查找服務

@Service  
public class UserLookupService {  

    private static final Logger logger = LoggerFactory.getLogger(UserLookupService.class);

    @Async
    public CompletableFuture<User> findUser(String user) throws InterruptedException {
        logger.info("Looking up " + user + ", thread: " + Thread.currentThread().getName());
        
        // mock result
        User results = new User();
        results.setName(user);
        results.setBlog("Blog - " + user);
        
        // Artificial delay of 1s for demonstration purposes
        Thread.sleep(1000L);
        return CompletableFuture.completedFuture(results);
    }

}

common.properties

cron.expression.reloadUserData=0 0/1 3-23 * * MON-SAT

歸功於 M. Deinum。

“刪除@PostConstruct,因為這可能會導致您的 UserLookupService 急切初始化導致沒有代理,因此沒有 @Async 行為”

這是一個有效的答案。

你可以參考:

@Service
public class MyService {

private static final Logger logger = LoggerFactory.getLogger(MyService.class);

@Autowired
private UserLookupService gitHubLookupService;

@Scheduled(cron = "${cron.expression.reloadUserData}")
@PostConstruct
public void loadCurrentDayUsers() throws Exception {

    this.loadUsers();
    // this.loadOthers();

}

@Autowired
@Qualifier("threadTaskGithubLookup")
private ThreadPoolTaskExecutor threadTaskGithubLookup;

public void loadUsers() throws Exception {

    // Start the clock
    long start = System.currentTimeMillis();

    List<String> stringList = new ArrayList<>();
    stringList.add("PivotalSoftware");
    stringList.add("CloudFoundry");
    stringList.add("Spring-Projects");

    List<CompletableFuture<User>> lstComplet = new ArrayList<>();

    for (String str : stringList) {
        lst.add(CompletableFuture.runAsync(() -> {
            try {
                lstComplet.add(doExecuted(str));
            } catch (Exception e){
                log.error("Exception {}", e.getMessage());
            }
        }, threadTaskGithubLookup));
    }

    List<User> userResults = new ArrayList<>();
    for(CompletableFuture<User> compl : lstComplet){
        User user = compl.get();
        userResult.add(user);
    }

    // Print results, including elapsed time
    logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
    for(User us : userResults){
        logger.info("--> " + new ObjectMapper().writeValueAsString(us));
    }
}

private CompletableFuture<User> doExecuted(String str) {
    User user = gitHubLookupService.findUser(str);
    return CompletableFuture.completedFuture(user);;
}

我重新聲明了 bean Executor:

@Bean("threadTaskGithubLookup")
public ThreadPoolTaskExecutor threadTaskGithubLookup() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.setMaxPoolSize(3);
    executor.setThreadNamePrefix("GithubLookup-");
    executor.initialize();
    return executor;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM