web-dev-qa-db-ja.com

複数の非同期リクエストを異なるWebサービスに送信する方法は?

多くの異なるWebサービスに複数のリクエストを送信し、結果を受け取る必要があります。問題は、リクエストを1つずつ送信すると、すべてを個別に送信して処理する必要がある限り、時間がかかることです。

すべてのリクエストを一度に送信して結果を受信する方法を知りたいです。

次のコードが示すように、私には3つの主要なメソッドがあり、それぞれに独自のサブメソッドがあります。各サブメソッドは、関連付けられたWebサービスにリクエストを送信して結果を受信します。したがって、たとえば、Webサービスの結果を受信するには、1から8までのすべてのWebサービスが完了するまで待機する必要があり、送信に長い時間がかかりますすべてのリクエストを1つずつ取得し、その結果を受け取ります。

以下に示すように、メソッドもサブメソッドも互いに関連していないため、それらをすべて呼び出して結果を任意の順序で受け取ることができます。重要なのは、各サブメソッドの結果を受け取ってデータを入力することだけです。関連リスト。

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

}
15
J888

これは単純なfork-joinアプローチですが、わかりやすくするために、このアプローチのように、任意の数のスレッドを開始して、後で使用可能な結果を​​取得できます。

_    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();
_

更新、完了:

これは冗長ですが実行可能な解決策です。私はその場限りでそれを書いて、それをコンパイルしていません。 3つのリストに異なるタイプがあり、WSメソッドが個別であることを考えると、それは実際にはモジュール化されていませんが、最高のプログラミングスキルを使用して、それを少しだけモジュール化できるかどうか試してください。

_    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();
_

Callableは結果のリストを返し、独自の個別のスレッドで呼び出されます。
Future.get()メソッドを呼び出すと、結果がメインスレッドに返されます。
結果は[〜#〜] not [〜#〜]Callableが完了するまで使用できるため、同時実行性の問題はありません。

25

だからちょうど楽しみのために私は2つの実用的な例を提供しています。最初の方法は、これを行う前の昔ながらの方法を示していますJava1.5。2番目の方法は、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);
            }
        }
    }
}

これははるかに単純な作業バージョンです。

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();
        }
    }
}
3
D-Klotz

_jax-ws_実装にWebサービスの非同期バインディングを生成するように要求できます。

これには、次の2つの利点があります。

  1. JAX-WSによる非同期Webサービス呼び出し:非同期にwsimportサポートを使用するか、独自にロールバックしますか? 、_jax-ws_は、十分にテストされた(そしておそらくより洗練された)コードを生成します。 ExecutorServiceを自分でインスタンス化する必要はありません。だからあなたのためのより少ない仕事! (ただし、スレッド化実装の詳細に対する制御が少なくなります)
  2. 生成されたバインディングには、コールバックハンドラーを指定するメソッドが含まれます。これは、get()を呼び出すスレッドですべての応答リストを同期的にretrieveAllLists()するよりもニーズに適しています。これにより、サービス呼び出しごとのエラー処理が可能になり、結果が並行して処理されます。これは、処理が重要な場合に適しています。

Metroの例は Metroサイトで です。カスタムバインディングファイル custom-client.xml の内容に注意してください。

_<bindings ...>    
    <bindings node="wsdl:definitions">
        <enableAsyncMapping>true</enableAsyncMapping>
    </bindings>    
</bindings>
_

このバインディングファイルをwsimportに指定すると、 _javax.xml.ws.Response<T>_ を実装するオブジェクトを返すクライアントが生成されます。 ResponseFutureインターフェースを拡張したもので、他の人も独自の実装をロールするときに使用することを提案しています。

したがって、当然のことながら、コールバックなしでコードを実行すると、コードは他の回答と同様になります。

_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());
}
_

次のようなコールバックハンドラーを作成する場合

_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);
        }
    }
}
_

次のように使用できます。

_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()
}
_

結果は同時に追加されるため、studentResultsコレクションはスレッドセーフである必要があることに注意してください。

1
flup

次のパラダイムで作業を(シリアルに)作成するとしますが、実際の作業は並行して行われます。これを行う1つの方法は次のとおりです。1)「メイン」に作業項目のキューを作成してもらいます。 2)実行する作業をキューに照会する「doWork」オブジェクトを作成します。 3)「メイン」にいくつかの「doWork」スレッドを開始させます(異なるサービスの数と同じ数でも、より少ない数でもかまいません)。 「doWork」オブジェクトを配置して、その結果をオブジェクトリストに追加します(どのコンストラクトでも機能するベクター、リスト...)。

各「doWork」オブジェクトは、キュー項目に完了のマークを付け、すべての結果を渡されたコンテナーに入れ、新しい作業をチェックします(キューにそれ以上ない場合は、スリープして再試行します)。

もちろん、クラスモデルをどれだけうまく構築できるかを確認する必要があります。各Webサービスの解析がまったく異なる場合は、「retrieveinfo」クラスのそれぞれが実装を約束するインターフェースを作成することができます。

1
ErstwhileIII

問題を見ると、アプリケーションを10以上の異なるWebサービスと統合する必要があります。すべての呼び出しを非同期にします。これは、Apache Camelで簡単に実行できます。エンタープライズ統合のための卓越したフレームワークであり、非同期処理もサポートしています。そのCXFコンポーネントを使用してWebサービスを呼び出し、そのルーティングエンジンを使用して呼び出しと結果の処理を行うことができます。キャメルの非同期ルーティング機能については、次の ページ をご覧ください。また、CXFを使用して非同期でwebservicesを呼び出す完全な例も提供しており、maven repo で入手できます。詳細については、次の ページ も参照してください。

1
Hussain Pirosha