Ich versuche, einen asynchronen Controller mit SprintBoot zu implementieren. Ich möchte eine REST-Anforderung an einen Controller senden, so dass der Controller sofort zurückkehrt, während die Arbeit auf dem Server fortgesetzt wird.Wie implementiert man eine asynchrone REST-Anfrage an einen Controller mit Springboot?
Ich folge diesem Frühling Beispiel: http://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support
Ich vermute, das ein Konfigurationsproblem ist. Kann mir bitte jemand sagen, was ich vermisse? Ich bin neu in Spring, also wenn Sie bitte so viele Details wie möglich zur Verfügung stellen könnten, wäre es zu schätzen.
eine Arbeits Controller habe ich die folgenden Änderungen:
// Before
@RequestMapping(method=RequestMethod.POST)
public String processUpload(final MultipartFile file) {
// ...
return "someView";
}
// After
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public Object call() throws Exception {
// ...
return "someView";
}
};
}
Ich kann den neuen Controller nennen, aber ich habe zwei Fragen unter:
- Der neue Controller nicht asynchron aufgerufen wird. Der Browser hängt während des Anrufs. Der Aufruf führt den Code jedoch aus.
Die Anforderungszeitüberschreitung mit diesem Fehler:
2015-03-06 16: 36: 10,592 ERROR 13012 --- [MvcAsync1] oswcrequest.async.WebAsyncManager: Es kann keine vollständige async Verarbeitung wegen Zeitüberschreitung oder Netzwerkfehler
Update: konnte ich das Timeout lösen, indem die folgende Bohne in meiner Anwendung Datei zu erstellen:
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setPort(9000);
connector.setAsyncTimeout(60000);
}
});
return factory;
}
Aber der Aufruf an die Steuerung ist immer noch nicht asynchron. Der Browser bleibt für die Dauer des Anrufs hängen.
Ich bin immer noch auf der Suche nach Hilfe, wie Sie einen REST-Aufruf an die Steuerung sofort zurückkehren, während Sie die Arbeit im Hintergrund erledigen.
Update II
Danke Dave. Ich habe versucht, eine asynchrone Methode in einer Bean zu implementieren.
Hier ist meine Anwendungsklasse:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setPort(9000);
connector.setAsyncTimeout(60000);
}
});
return factory;
}
}
Hier meine Bean-Klasse ist:
public class LongProcess {
@Async
public Future<String> call() {
try {
System.out.println("Sleeping now...");
Thread.sleep(10000);
return new AsyncResult<String>("Hey");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
Meine Konfigurationsklasse:
@Configuration
@EnableAsync
public class LongProcessConfiguration implements AsyncConfigurer {
@Bean
public LongProcess longProcessBean() {
return new LongProcess();
}
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(10);
taskExecutor.setThreadNamePrefix("LULExecutor-");
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
Mein Controller-Methode:
@RequestMapping("/utilities/longProcess")
public String longProcess() {
System.out.println("Starting long process...");
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
Future<String> result = process.call();
System.out.println("Done!");
return "{success: 1}";
}
Dies kommt leider immer noch nicht sofort zurück. Beachten Sie, dass mir das Ergebnis von LongProcess nicht wichtig ist. Die Methode wird erfolgreich, aber nicht im Hintergrund aufgerufen. Irgendeine Idee, was ich vermisse?
@RequestMapping("/utilities/longProcess")
public String longProcess() throws InterruptedException {
System.out.println("Starting long process...");
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
Future<String> result = process.call();
while (!(result.isDone())) {
Thread.sleep(1); //10-millisecond pause between each check
System.out.println("Waiting for Long Process...");
}
System.out.println("Done!");
return "{success: 1}";
}
Update-III
I ersetzt
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
:
Als Test, wenn ich die Controller-Methode zu warten, bis das Ergebnis zu ändern, wird der Warteblock nie eingegeben
mit
@Autowired
private LongProcess process;
und das Problem gelöst.
Danke Dave. Ich habe Ihren Vorschlag versucht (siehe Update oben), aber der Anruf wird immer noch nicht im Hintergrund ausgeführt. Irgendwelche Vorschläge geschätzt. –
Sieht aus, als hättest du es repariert (der 'LongProcess' muss ein' @Bean' sein). Wie wäre es mit der Antwort? –
@DaveSyer Ich rufe Async auf andere Bean, aber mein RestService blockiert immer noch. Code hier ... Bean https://pastebin.com/ZejaaUcg Konfiguration https://pastebin.com/Dz6aFya4 Service https://pastebin.com/wbRLrupd – Zaknafein