Proprio in generale, è necessario incapsulare le unità di lavoro in un Runnable
o java.util.concurrent.Callable
ed eseguirle tramite java.util.concurrent.Executor
(o org.springframework.core.task.TaskExecutor
). Ciò consente a ciascuna unità di lavoro di essere eseguita separatamente, in genere in modo asincrono (a seconda dell'implementazione di Executor
).
Quindi per la vostra problema specifico, si potrebbe fare qualcosa di simile:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
//inject this
private Executor executor;
@RequestMapping("/your/path/here")
public String myMVCControllerGETdataMethod(Model model) {
//define all async requests and give them to injected Executor
List<GetRequestTask> tasks = new ArrayList<GetRequestTask>();
tasks.add(new GetRequestTask("http://api/data?type=1", this.executor));
tasks.add(new GetRequestTask("http://api/data?type=2", this.executor));
//...
//do other work here
//...
//now wait for all async tasks to complete
while(!tasks.isEmpty()) {
for(Iterator<GetRequestTask> it = tasks.iterator(); it.hasNext();) {
GetRequestTask task = it.next();
if(task.isDone()) {
String request = task.getRequest();
String response = task.getResponse();
//PUT YOUR CODE HERE
//possibly aggregate request and response in Map<String,String>
//or do something else with request and response
it.remove();
}
}
//avoid tight loop in "main" thread
if(!tasks.isEmpty()) Thread.sleep(100);
}
//now you have all responses for all async requests
//the following from your original code
//note: you should probably pass the responses from above
//to this next method (to keep your controller stateless)
String results = doWorkwithMultipleDataReturned();
model.addAttribute(results, results);
return "index";
}
//abstraction to wrap Callable and Future
class GetRequestTask {
private GetRequestWork work;
private FutureTask<String> task;
public GetRequestTask(String url, Executor executor) {
this.work = new GetRequestWork(url);
this.task = new FutureTask<String>(work);
executor.execute(this.task);
}
public String getRequest() {
return this.work.getUrl();
}
public boolean isDone() {
return this.task.isDone();
}
public String getResponse() {
try {
return this.task.get();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
//Callable representing actual HTTP GET request
class GetRequestWork implements Callable<String> {
private final String url;
public GetRequestWork(String url) {
this.url = url;
}
public String getUrl() {
return this.url;
}
public String call() throws Exception {
return new DefaultHttpClient().execute(new HttpGet(getUrl()), new BasicResponseHandler());
}
}
}
Si noti che questo codice non è stato testato.
Per l'implementazione Executor
, vedere Spring's TaskExecutor e task:executor namespace. Probabilmente vuoi un pool riutilizzabile di thread per questo caso d'uso (invece di creare un nuovo thread ogni volta).
Ah, molto cool! Darò questo un test drive. Grazie! Tuttavia, una domanda che ho è come faccio a sapere qual è la risposta nel ciclo iterativo? Inoltre, che cosa significa mantenere il mio controller stateless passando i risultati al metodo doWorkwithMultipleDataReturned()? – JaJ
Il codice di esempio consente di abbinare la richiesta originale (URL) con la risposta tramite l'astrazione 'GetRequestTask'. Quindi alla riga '// PUT YOUR CODE HERE' hai già entrambi come stringhe. Per quanto riguarda il commento stateless, stavo assumendo poiché il tuo metodo 'doWorkwithMultipleDataReturned' non ha accettato argomenti che potresti contenere le risposte in una variabile di istanza del controller, il che rende lo stato del controller (limitando l'uso della stessa istanza su più thread) . Piuttosto, dovresti mantenere i riferimenti alle risposte solo come variabili di metodo per evitare questo problema. – superEb
Ottimi punti! Grazie ancora per le informazioni! – JaJ