La méthode Spring Boot @Async dans le contrôleur s'exécute de manière synchrone
mon application [basic] Spring Boot accepte une requête du navigateur, envoyée via jQuery.get()
et est censé recevoir immédiatement une réponse, comme " votre demande a été mise en attente". Pour ce faire, j'ai écrit un contrôleur:
@Controller
public class DoSomeWorkController {
@Autowired
private final DoWorkService workService;
@RequestMapping("/doSomeWork")
@ResponseBody
public String doSomeWork() {
workService.doWork(); // time consuming operation
return "Your request has been queued.";
}
}
DoWorkServiceImpl
la classe implémente DoWorkService
interface et est vraiment simple. Il dispose d'une méthode unique pour effectuer une tâche fastidieuse. Je n'ai pas besoin d'un retour de cet appel de service, car un e-mail sera livré à la fin du travail, à la fois pour les scénarios d'échec ou de succès. Ainsi, cela ressemblerait effectivement à:
@Service
public class DoWorkServiceImpl implements DoWorkService {
@Async("workExecutor")
@Override
public void doWork() {
try {
Thread.sleep(10 * 1000);
System.out.println("completed work, sent email");
}
catch (InterruptedException ie) {
System.err.println(ie.getMessage());
}
}
}
je pensais que cela fonctionnerait, mais la requête Ajax du navigateur a attendu 10 secondes avant de retourner la réponse. Ainsi, la méthode mapped controller appelle la méthode interne annotée avec @Async
en mode synchrone, il semblerait. Dans une application Spring traditionnelle, j'ajoute typiquement ceci à la configuration XML:
<task:annotation-driven />
<task:executor id="workExecutor" pool-size="1" queue-capacity="0" rejection-policy="DISCARD" />
alors j'ai pensé écrire le l'équivalent de ceci dans la classe d'application principale aiderait:
@SpringBootApplication
@EnableAsync
public class Application {
@Value("${pool.size:1}")
private int poolSize;;
@Value("${queue.capacity:0}")
private int queueCapacity;
@Bean(name="workExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(poolSize);
taskExecutor.setQueueCapacity(queueCapacity);
taskExecutor.afterPropertiesSet();
return taskExecutor;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
cela n'a pas changé le comportement. La réponse Ajax arrive toujours après 10 Secondes d'envoi de la requête. Ce qui me manque?
L'application de démarrage à ressort peut être télécharger ici. Avec Maven installé, le projet peut être exécuté avec la commande simple:
mvn clean spring-boot:run
Remarque: le problème a été résolu grâce à la réponse fournie par @Dave Syer ci-dessous, qui a fait remarquer que je manquais @EnableAsync
dans mon application, même si j'avais la ligne dans l'extrait de code ci-dessus.
4 réponses
Vous appelez l' @Async
méthode à partir d'une autre méthode de la même classe. Sauf si vous activez le mode proxy D'AspectJ pour @EnableAsync
(et de fournir un tisserand, bien sûr) qui ne fonctionne pas (google "proxy auto-invocation"). La meilleure solution est de mettre le @Async
méthode dans un autre @Bean
.
Pour tous ceux qui sont toujours à la recherche de toutes les étapes de @Asnyc expliqué d'une manière simple, voici la réponse:
Voici un exemple simple avec @Asynchrone. Suivez ces étapes pour que @Async fonctionne dans votre application de démarrage:
Étape 1: Ajouter @ EnableAsync annotation et Ajouter Taskexecutor Bean à la classe D'Application.
Exemple:
@SpringBootApplication
@EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);
@Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(AsynchronousSpringBootApplication.class,args);
}
}
Étape 2: Ajouter une méthode qui exécute un processus asynchrone
@Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
@Async("processExecutor")
@Override
public void process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Étape 3: Ajouter une API dans le contrôleur pour exécuter le traitement asynchrone
@Autowired
private ProcessService processService;
@RequestMapping(value = "ping/async", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> async() {
processService.process();
Map<String, String> response = new HashMap<>();
response.put("message", "Request is under process");
return new ResponseEntity<>(response, HttpStatus.OK);
}
j'ai aussi écrit un blog et une application de travail sur GitHub avec ces étapes. Veuillez vérifier: http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html
j'ai eu un problème similaire et j'ai eu les annotations @Async et @EnableAsync dans les haricots corrects et toujours la méthode a été l'exécution synchrone. Après que j'ai vérifié les logs il y avait un avertissement disant que j'avais plus d'un haricot de type ThreadPoolTaskExecutor et aucun d'eux appelé taskExecutor...
@Bean(name="taskExecutor")
public ThreadPoolTaskExecutor defaultTaskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
//Thread pool configuration
//...
return pool;
}
Voir http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html pour la configuration disponible pour le pool de threads.
Suivez les trois étapes suivantes :
1 Step : Utilisez @EnableAsync avec @configuration ou @SpringBootApplication
@EnableAsync Public class Application {
2 Étape :
/**
* THIS FOR ASYNCRONOUS PROCESS/METHOD
* @return
*/
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Anycronous Process-");
executor.initialize();
return executor;
}
Étape 3: @Async sur la methode
T