2015-10-16 122 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

谢谢!我将有一个发挥:) –