2015-10-16 130 views
0

我在這裏看到了一些類似的問題,但是他們的解決方案都沒有工作。我的2個異步調用同步運行。Spring Boot @Async同步運行

HomeController.java

@Controller 
@EnableOAuth2Sso 
public class HomeController { 

@Autowired 
private ProjectService projectService; 

@RequestMapping("/") 
public String home(Model model) { 
    Future<List<Project>> latest = projectService.getLatest(); 
    Future<List<Project>> popular = projectService.getPopular(); 

    try { 
     while (!(latest.isDone() && popular.isDone())) { 
      Thread.sleep(10); //10-millisecond pause between each check 
     } 

     model.addAttribute("latest", latest.get()); 
     model.addAttribute("popular", popular.get()); 
    } catch(Exception e) { 
     System.out.println("ERROR in async thread " + e.getMessage()); 
    } 

    return "home"; 
} 
} 

ProjectService.java

@Service 
public class ProjectService { 

@Autowired 
private MessageSendingOperations<String> messagingTemplate; 

@Autowired 
private ProjectRepository projectRepository; 

@Async("taskExecutor") 
public Future<List<Project>> getLatest() { 
    return new AsyncResult<List<Project>>(this.projectRepository.getLatest()); 
} 

@Async("taskExecutor") 
public Future<List<Project>> getPopular() { 
    return new AsyncResult<List<Project>>(this.projectRepository.getPopular()); 
} 

ProjectRepository.java

@Component 
public class ProjectRepository { 

public List<Project> getLatest() { 
    return this.generateData(); // 10 seconds to generate random data 
} 

public List<Project> getPopular() { 
    return this.generateData(); // 10 seconds to generate random data 
} 

public List<Project> generateData() { 
    try { Thread.sleep(10000); } catch(Exception e) {} 

    ArrayList<Project> projects = new ArrayList<Project>(); 
    ArrayList<Repository> repositories = new ArrayList<Repository>(); 

    repositories.add(
      new Repository("repo-name1", "repo-url-1") 
    ); 
    repositories.add(
      new Repository("repo-name2", "repo-url-2") 
    ); 

    projects.add(
      new Project("title10", "description10") 
        .setViews(new Random().nextInt(10000)) 
        .setRepositories(repositories) 
    ); 
    projects.add(new Project("title20", "description20").setViews(new Random().nextInt(1000))); 

    projects.add(new Project("title", UUID.randomUUID().toString()).setViews(new Random().nextInt(100))); 

    return projects; 
} 
} 

AsyncConfig.java

@Configuration 
@EnableAsync 
public class AsyncConfig { 

@Value("${pool.size:10}") 
private int poolSize;; 

@Value("${queue.capacity:10}") 
private int queueCapacity; 

@Bean(name="taskExecutor") 
public TaskExecutor taskExecutor() { 
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); 
    taskExecutor.setMaxPoolSize(this.poolSize); 
    taskExecutor.setQueueCapacity(this.queueCapacity); 
    taskExecutor.afterPropertiesSet(); 
    return new ThreadPoolTaskExecutor(); 
} 
} 

服務都從控制器呼籲應採取大約10秒鐘,如果運行的異步,但它仍然需要20秒 - 我失去了什麼?

PS。忽略簡單的愚蠢,沒有接口等,我儘可能地去除了嘗試和隔離問題。我原本跟着官方的文檔https://spring.io/guides/gs/async-method/

代碼在GitHub上:https://github.com/DashboardHub/PipelineDashboard/tree/feature/homepage

+0

你可以嘗試用Thread.sleep(10000)代替this.generateData()並告訴我們會發生什麼?如果generateData()被CPU綁定,並且線程被分配到相同的核心,則即使兩個任務運行異步,總時間可能也是20秒。 – Giovanni

+0

我已經添加了包含睡眠的我的generateData()方法。您需要的任何其他信息? –

+0

你能把這個代碼推到github上嗎?所以玩起來會更容易。如果你使用CompletableFuture,它會更具可讀性 – freakman

回答

1

這是基於你的代碼的簡單和工作測試用例(它適合於命令行中運行):

package test.springAsync; 

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.task.TaskExecutor; 
import org.springframework.scheduling.annotation.EnableAsync; 
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 

@Configuration 
@ComponentScan(basePackages = "test.springAsync") 
@EnableAsync 
public class AsyncConfig { 

private int poolSize=10; 

private int queueCapacity=10; 

    @Bean(name = "taskExecutor1") 
    public TaskExecutor taskExecutor() { 
     ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); 
     taskExecutor.setCorePoolSize(10); 
     taskExecutor.setMaxPoolSize(this.poolSize); 
     taskExecutor.setQueueCapacity(this.queueCapacity); 
     taskExecutor.afterPropertiesSet(); 
     taskExecutor.setThreadNamePrefix("testExecutor"); 
     return taskExecutor; 
    } 
} 

工程類

package test.springAsync; 

public class Project { 
    private String name; 

    public Project(String title, String description) { 

    } 

    public String getName() { 
     return name; 
    } 

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

    public Project setViews(int value) { 
     return this; 
    } 

} 

ProjectRepository類:

package test.springAsync; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 
import java.util.UUID; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.stereotype.Component; 
@Component 
public class ProjectRepository { 
    private Logger logger=LoggerFactory.getLogger(ProjectRepository.class.getName()); 

    public List<Project> getLatest() { 
     return this.generateData(); // 10 seconds to generate random data 
    } 

    public List<Project> getPopular() { 
     return this.generateData(); // 10 seconds to generate random data 
    } 

    public List<Project> generateData() { 
     logger.debug("generateData start"); 
     try { 
      Thread.sleep(10000); 
     } catch (Exception e) { 
     } 

     ArrayList<Project> projects = new ArrayList<Project>(); 


     projects.add(new Project("title10", "description10").setViews(new Random().nextInt(10000))); 
     projects.add(new Project("title20", "description20").setViews(new Random().nextInt(1000))); 

     projects.add(new Project("title", UUID.randomUUID().toString()).setViews(new Random().nextInt(100))); 
     logger.debug("generateData end"); 
     return projects; 
    } 
} 

ProjectService類:

package test.springAsync; 

import java.util.List; 
import java.util.concurrent.Future; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.scheduling.annotation.Async; 
import org.springframework.scheduling.annotation.AsyncResult; 
import org.springframework.stereotype.Service; 

@Service 
public class ProjectService { 


    @Autowired 
    private ProjectRepository projectRepository; 

    @Async("taskExecutor1") 
    public Future<List<Project>> getLatest() { 
     return new AsyncResult<List<Project>>(this.projectRepository.getLatest()); 
    } 

    @Async("taskExecutor1") 
    public Future<List<Project>> getPopular() { 
     return new AsyncResult<List<Project>>(this.projectRepository.getPopular()); 
    } 
} 

TestAsyncBean我起訴全日空替代你的控制器:

package test.springAsync; 

import java.util.List; 
import java.util.concurrent.Future; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 

@Component 
public class TestAsyncBean { 
    private Logger logger=LoggerFactory.getLogger(TestAsyncBean.class.getName()); 
    @Autowired 
    private ProjectService projectService; 
    public void home() { 
     logger.debug("start Home"); 
     Future<List<Project>> latest = projectService.getLatest(); 
     Future<List<Project>> popular = projectService.getPopular(); 

     try { 
      while (!(latest.isDone() && popular.isDone())) { 
       Thread.sleep(1000); //10-millisecond pause between each check 
       logger.debug("waiting for AsyncMethods"); 
      } 
      logger.debug("AsyncMethods did the job"); 
     } catch(Exception e) { 
      System.out.println("ERROR in async thread " + e.getMessage()); 
     } 

    } 
} 

TestMain我用藏在心裏在一起類:

package test.springAsync; 

import org.springframework.context.ApplicationContext; 
import org.springframework.context.annotation.AnnotationConfigApplicationContext; 

public class TestMain { 

    public static void main(String[] args) { 
     ApplicationContext ctx = new AnnotationConfigApplicationContext(AsyncConfig.class); 
     TestAsyncBean myService = ctx.getBean(TestAsyncBean.class); 
     myService.home(); 

    } 

} 

最後這裏是mai的輸出n:

2015-10-16 09:16:19,231 [main] DEBUG test.springAsync.TestAsyncBean - start Home 
2015-10-16 09:16:19,244 [testExecutor1] DEBUG test.springAsync.ProjectRepository - generateData start 
2015-10-16 09:16:19,244 [testExecutor2] DEBUG test.springAsync.ProjectRepository - generateData start 
2015-10-16 09:16:20,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:21,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:22,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:23,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:24,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:25,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:26,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:27,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:28,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:29,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:29,298 [testExecutor2] DEBUG test.springAsync.ProjectRepository - generateData end 
2015-10-16 09:16:29,298 [testExecutor1] DEBUG test.springAsync.ProjectRepository - generateData end 
2015-10-16 09:16:30,236 [main] DEBUG test.springAsync.TestAsyncBean - waiting for AsyncMethods 
2015-10-16 09:16:30,236 [main] DEBUG test.springAsync.TestAsyncBean - AsyncMethods did the job 

正如你可以看到frrm日誌,兩個線程開始並行運行約11秒後終止。我建議你在你的代碼中加入一些日誌,並從命令行創建一個可運行的樣本,以便輕鬆診斷出現問題

+0

是的,對不起,這是從我的複製/粘貼錯誤,因爲我部分是通過改變某些東西。我檢查了,我正在返回'taskExecutor' –

+0

@EddieJaoude我重寫了我的答案,可以從命令行運行的一個工作示例或ide – Giovanni

+0

謝謝!我將有一個發揮:) –