Je dois envoyer plusieurs demandes à de nombreux services Web et recevoir les résultats. Le problème est que, si j'envoie les demandes une par une, cela prend aussi longtemps que j'ai besoin d'envoyer et de traiter toutes les requêtes individuellement.
Je me demande comment je peux envoyer toutes les demandes en même temps et recevoir les résultats.
Comme le code suivant le montre, j'ai trois méthodes principales et chacune a ses propres méthodes. Chaque sous-méthode envoie une requête à son service Web associé et reçoit les résultats. Par exemple, pour recevoir les résultats du service Web 9, je dois attendre que tous les services Web de 1 à 8 soient terminés, l'envoi prend beaucoup de temps. toutes les demandes une par une et recevoir leurs résultats.
Comme indiqué ci-dessous, aucune des méthodes, ni des sous-méthodes, ne sont liées les unes aux autres. Je peux donc les appeler toutes et recevoir leurs résultats dans n'importe quel ordre. La seule chose qui importe est de recevoir les résultats de chaque sous-méthode et de les renseigner. listes associées.
private List<StudentsResults> studentsResults = new ArrayList();
private List<DoctorsResults> doctorsResults = new ArrayList();
private List<PatientsResults> patientsResults = new ArrayList();
main (){
retrieveAllLists();
}
retrieveAllLists(){
retrieveStudents();
retrieveDoctors();
retrievePatients();
}
retrieveStudents(){
this.studentsResults = retrieveStdWS1(); //send request to Web Service 1 to receive its list of students
this.studentsResults = retrieveStdWS2(); //send request to Web Service 2 to receive its list of students
this.studentsResults = retrieveStdWS3(); //send request to Web Service 3 to receive its list of students
}
retrieveDoctors(){
this.doctorsResults = retrieveDocWS4(); //send request to Web Service 4 to receive its list of doctors
this.doctorsResults = retrieveDocWS5(); //send request to Web Service 5 to receive its list of doctors
this.doctorsResults = retrieveDocWS6(); //send request to Web Service 6 to receive its list of doctors
}
retrievePatients(){
this.patientsResults = retrievePtWS7(); //send request to Web Service 7 to receive its list of patients
this.patientsResults = retrievePtWS8(); //send request to Web Service 8 to receive its list of patients
this.patientsResults = retrievePtWS9(); //send request to Web Service 9 to receive its list of patients
}
Il s’agit d’une approche simple de type fork-join, mais par souci de clarté, vous pouvez démarrer n’importe quel nombre de threads et récupérer les résultats ultérieurement, au fur et à mesure de leur disponibilité, comme cette approche.
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new Callable<String>() {
public String call() throws Exception {
Thread.sleep((new Random().nextInt(5000)) + 500);
return "Hello world";
}
});
List<Future<String>> results = pool.invokeAll(tasks);
for (Future<String> future : results) {
System.out.println(future.get());
}
pool.shutdown();
MISE À JOUR TERMINÉE:
Voici une solution verbeuse, mais réalisable. Je l'ai écrit ad hoc et je ne l'ai pas compilé. Étant donné que les trois listes ont des types différents, et que les méthodes WS sont individuelles, ce n'est pas Vraiment modulaire, mais essayez d'utiliser vos meilleures compétences en programmation et voyez si vous pouvez le modéliser un peu mieux.
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Callable<List<StudentsResults>>> stasks = new ArrayList<>();
List<Callable<List<DoctorsResults>>> dtasks = new ArrayList<>();
List<Callable<List<PatientsResults>>> ptasks = new ArrayList<>();
stasks.add(new Callable<List<StudentsResults>>() {
public List<StudentsResults> call() throws Exception {
return retrieveStdWS1();
}
});
stasks.add(new Callable<List<StudentsResults>>() {
public List<StudentsResults> call() throws Exception {
return retrieveStdWS2();
}
});
stasks.add(new Callable<List<StudentsResults>>() {
public List<StudentsResults> call() throws Exception {
return retrieveStdWS3();
}
});
dtasks.add(new Callable<List<DoctorsResults>>() {
public List<DoctorsResults> call() throws Exception {
return retrieveDocWS4();
}
});
dtasks.add(new Callable<List<DoctorsResults>>() {
public List<DoctorsResults> call() throws Exception {
return retrieveDocWS5();
}
});
dtasks.add(new Callable<List<DoctorsResults>>() {
public List<DoctorsResults> call() throws Exception {
return retrieveDocWS6();
}
});
ptasks.add(new Callable<List<PatientsResults>>() {
public List<PatientsResults> call() throws Exception {
return retrievePtWS7();
}
});
ptasks.add(new Callable<List<PatientsResults>>() {
public List<PatientsResults> call() throws Exception {
return retrievePtWS8();
}
});
ptasks.add(new Callable<List<PatientsResults>>() {
public List<PatientsResults> call() throws Exception {
return retrievePtWS9();
}
});
List<Future<List<StudentsResults>>> sresults = pool.invokeAll(stasks);
List<Future<List<DoctorsResults>>> dresults = pool.invokeAll(dtasks);
List<Future<List<PatientsResults>>> presults = pool.invokeAll(ptasks);
for (Future<List<StudentsResults>> future : sresults) {
this.studentsResults.addAll(future.get());
}
for (Future<List<DoctorsResults>> future : dresults) {
this.doctorsResults.addAll(future.get());
}
for (Future<List<PatientsResults>> future : presults) {
this.patientsResults.addAll(future.get());
}
pool.shutdown();
Chaque Callable
renvoie une liste de résultats et est appelé dans son propre thread séparé.
Lorsque vous appelez la méthode Future.get()
, vous obtenez le résultat sur le thread principal.
Le résultat estPASdisponible jusqu'à la fin de la Callable
; il n'y a donc pas de problème de simultanéité.
Donc, juste pour le plaisir, je fournis deux exemples de travail. La première montre la manière de faire de l’ancienne école avant Java 1.5. La seconde montre une manière beaucoup plus propre en utilisant les outils disponibles dans Java 1.5:
import Java.util.ArrayList;
public class ThreadingExample
{
private ArrayList <MyThread> myThreads;
public static class MyRunnable implements Runnable
{
private String data;
public String getData()
{
return data;
}
public void setData(String data)
{
this.data = data;
}
@Override
public void run()
{
}
}
public static class MyThread extends Thread
{
private MyRunnable myRunnable;
MyThread(MyRunnable runnable)
{
super(runnable);
setMyRunnable(runnable);
}
/**
* @return the myRunnable
*/
public MyRunnable getMyRunnable()
{
return myRunnable;
}
/**
* @param myRunnable the myRunnable to set
*/
public void setMyRunnable(MyRunnable myRunnable)
{
this.myRunnable = myRunnable;
}
}
public ThreadingExample()
{
myThreads = new ArrayList <MyThread> ();
}
public ArrayList <String> retrieveMyData ()
{
ArrayList <String> allmyData = new ArrayList <String> ();
if (isComplete() == false)
{
// Sadly we aren't done
return (null);
}
for (MyThread myThread : myThreads)
{
allmyData.add(myThread.getMyRunnable().getData());
}
return (allmyData);
}
private boolean isComplete()
{
boolean complete = true;
// wait for all of them to finish
for (MyThread x : myThreads)
{
if (x.isAlive())
{
complete = false;
break;
}
}
return (complete);
}
public void kickOffQueries()
{
myThreads.clear();
MyThread a = new MyThread(new MyRunnable()
{
@Override
public void run()
{
// This is where you make the call to external services
// giving the results to setData("");
setData("Data from list A");
}
});
myThreads.add(a);
MyThread b = new MyThread (new MyRunnable()
{
@Override
public void run()
{
// This is where you make the call to external services
// giving the results to setData("");
setData("Data from list B");
}
});
myThreads.add(b);
for (MyThread x : myThreads)
{
x.start();
}
boolean done = false;
while (done == false)
{
if (isComplete())
{
done = true;
}
else
{
// Sleep for 10 milliseconds
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
public static void main(String [] args)
{
ThreadingExample example = new ThreadingExample();
example.kickOffQueries();
ArrayList <String> data = example.retrieveMyData();
if (data != null)
{
for (String s : data)
{
System.out.println (s);
}
}
}
}
C'est la version de travail beaucoup plus simple:
import Java.util.HashSet;
import Java.util.List;
import Java.util.Set;
import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
public class ThreadingExample
{
public static void main(String [] args)
{
ExecutorService service = Executors.newCachedThreadPool();
Set <Callable<String>> callables = new HashSet <Callable<String>> ();
callables.add(new Callable<String>()
{
@Override
public String call() throws Exception
{
return "This is where I make the call to web service A, and put its results here";
}
});
callables.add(new Callable<String>()
{
@Override
public String call() throws Exception
{
return "This is where I make the call to web service B, and put its results here";
}
});
callables.add(new Callable<String>()
{
@Override
public String call() throws Exception
{
return "This is where I make the call to web service C, and put its results here";
}
});
try
{
List<Future<String>> futures = service.invokeAll(callables);
for (Future<String> future : futures)
{
System.out.println (future.get());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
}
Vous pouvez demander à votre implémentation jax-ws
de générer des liaisons asynchrones pour le service Web.
Cela a deux avantages que je peux voir:
jax-ws
générera pour vous un code bien testé (et peut-être plus sophistiqué); vous n'avez pas besoin d'instancier vous-même le service d'exécution (ExecutorService). Donc, moins de travail pour vous! (mais aussi moins de contrôle sur les détails d'implémentation du threading)get()
de manière synchrone, en affichant toutes les listes de réponses sur le thread appelant retrieveAllLists()
. Il permet le traitement des erreurs par appel de service et traite les résultats en parallèle, ce qui est agréable si le traitement n'est pas trivial.Vous trouverez un exemple pour Metro sur le site Metro . Notez le contenu du fichier de liaisons personnalisées custom-client.xml :
<bindings ...>
<bindings node="wsdl:definitions">
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
</bindings>
Lorsque vous spécifiez ce fichier de liaisons sur wsimport
, il génère un client qui renvoie un objet qui implémente javax.xml.ws.Response<T>
. Response
étend l'interface Future
que d'autres suggèrent également d'utiliser lors de la mise en œuvre de votre propre implémentation.
Donc, sans surprise, si vous vous en sortez, le code ressemblera aux autres réponses:
public void retrieveAllLists() throws ExecutionException{
// first fire all requests
Response<List<StudentsResults>> students1 = ws1.getStudents();
Response<List<StudentsResults>> students2 = ws2.getStudents();
Response<List<StudentsResults>> students3 = ws3.getStudents();
Response<List<DoctorsResults>> doctors1 = ws4.getDoctors();
Response<List<DoctorsResults>> doctors2 = ws5.getDoctors();
Response<List<DoctorsResults>> doctors3 = ws6.getDoctors();
Response<List<PatientsResults>> patients1 = ws7.getPatients();
Response<List<PatientsResults>> patients2 = ws8.getPatients();
Response<List<PatientsResults>> patients3 = ws9.getPatients();
// then await and collect all the responses
studentsResults.addAll(students1.get());
studentsResults.addAll(students2.get());
studentsResults.addAll(students3.get());
doctorsResults.addAll(doctors1.get());
doctorsResults.addAll(doctors2.get());
doctorsResults.addAll(doctors3.get());
patientsResults.addAll(patients1.get());
patientsResults.addAll(patients2.get());
patientsResults.addAll(patients3.get());
}
Si vous créez des handers de rappel tels que
private class StudentsCallbackHandler
implements AsyncHandler<Response<List<StudentsResults>>> {
public void handleResponse(List<StudentsResults> response) {
try {
studentsResults.addAll(response.get());
} catch (ExecutionException e) {
errors.add(new CustomError("Failed to retrieve Students.", e.getCause()));
} catch (InterruptedException e) {
log.error("Interrupted", e);
}
}
}
vous pouvez les utiliser comme ceci:
public void retrieveAllLists() {
List<Future<?>> responses = new ArrayList<Future<?>>();
// fire all requests, specifying callback handlers
responses.add(ws1.getStudents(new StudentsCallbackHandler()));
responses.add(ws2.getStudents(new StudentsCallbackHandler()));
responses.add(ws3.getStudents(new StudentsCallbackHandler()));
...
// await completion
for( Future<?> response: responses ) {
response.get();
}
// or do some other work, and poll response.isDone()
}
Notez que la collection studentResults doit être thread-safe maintenant, car les résultats seront ajoutés simultanément!
En examinant le problème, vous devez intégrer votre application à plus de 10 services Web différents. Tout en rendant tous les appels asynchrones. Cela peut être fait facilement avec Apache Camel. C'est un cadre important pour l'intégration d'entreprise et prend également en charge le traitement asynchrone. Vous pouvez utiliser son composant CXF pour appeler des services Web et son moteur de routage pour invoquer et traiter les résultats. Regardez la page suivante concernant la capacité de routage asynchrone du chameau. Ils ont également fourni un exemple complet appelant async Webservices utilisant CXF, disponible sur son maven repo . Consultez également le page suivant pour plus de détails.
Vous pouvez envisager le paradigme suivant dans lequel vous créez un travail (en série), mais le travail réel est effectué en parallèle. Une façon de procéder consiste à: 1) laisser votre "principal" créer une file d’éléments de travail; 2) créer un objet "doWork" qui interroge la file d'attente sur le travail à effectuer; 3) avoir "principal" démarrer un certain nombre de threads "doWork" (peut être le même nombre que le nombre de services différents, ou un nombre plus petit); demandez aux objets "doWork" d'ajouter leurs résultats à une liste d'objets (quelle que soit la construction qui fonctionne Vector, liste ...).
Chaque objet "doWork" marquerait son élément de file d'attente comme étant terminé, mettrait tous les résultats dans le conteneur transmis et rechercherait un nouveau travail (s'il n'y en avait plus, il dormait et réessayait).
Bien sûr, vous voudrez voir dans quelle mesure vous pouvez construire votre modèle de classe. Si chacun des services Web est assez différent pour l'analyse, vous pouvez créer une interface que chacune de vos classes "retrieveinfo" promet d'implémenter.