web-dev-qa-db-ja.com

SpringスレッドとTaskExecutorを使用して、スレッドがいつ終了したかを知るにはどうすればよいですか?

わかりました、ここでは単純な質問の可能性があります。複数のネットワークデバイスにログインし、それぞれにコマンドを実行して結果を収集する必要があるサービスがあります。速度を上げるために、各デバイスの情報を順番に収集するのではなく、それらすべてに同時にアクセスして、完了後に結果を消費する必要があります。

SpringフレームワークとJschを使用すると、各デバイスを正しくクエリするのが非常に簡単になります。私が混乱しているのは、TaskExecutorを使用してこれを実現するためにBeanを再配線しようとしているところです。どうすればよいかわからないのは、スレッドがいつ終了したかを知る方法です。

私がこれまで持っているのはこれです:

public class RemoteCommand {

    private String user;
    private String Host;
    private String password;
    private String command;
    private List<String> commandResults;
    private TaskExecutor taskExecutor;

    public RemoteCommand(String user, String Host, String password, TaskExecutor taskExecutor) {

        setUser(user);
        setHost(Host);
        setPassword(password);
        setTaskExecutor(taskExecutor);
    }

    /**
     * @param user the user to set
     */
    public void setUser(String user) {
        this.user = user;
    }

    /**
     * @return the user
     */
    public String getUser() {
        return user;
    }

    /**
     * @param Host the Host to set
     */
    public void setHost(String Host) {
        this.Host = Host;
    }

    /**
     * @return the Host
     */
    public String getHost() {
        return Host;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }

    /**
     * @param command the command to set
     */
    private void setCommand(String command) {
        this.command = command;
    }

    /**
     * @return the command
     */
    private String getCommand() {
        return command;
    }

    /**
     * @param commandResults the commandResults to set
     */
    private void setCommandResults(List<String> commandResults) {
        this.commandResults = commandResults;
    }

    /**
     * @return the commandResults
     */
    public List<String> getCommandResults(String command) {
        taskExecutor.execute(new CommandTask(command) );

        return commandResults;
    }

    /**
     * @param taskExecutor the taskExecutor to set
     */
    public void setTaskExecutor(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    /**
     * @return the taskExecutor
     */
    public TaskExecutor getTaskExecutor() {
        return taskExecutor;
    }

    private class CommandTask implements Runnable {

        public CommandTask(String command) {
            setCommand(command);
            System.out.println("test: " + getCommand());
        }

        /**
         * 
         * @param command
         */
        public void run() {

            List<String> results = new LinkedList<String>();
            String command = getCommand();

            try {
                System.out.println("running");
                JSch jsch = new JSch();

                String user = getUser();
                String Host = getHost();

                Java.util.Properties config = new Java.util.Properties(); 
                config.put("StrictHostKeyChecking", "no");

                Host = Host.substring(Host.indexOf('@') + 1);
                Session session = jsch.getSession(user, Host, 22);

                session.setPassword(getPassword());
                session.setConfig(config);
                session.connect();

                Channel channel = session.openChannel("exec");
                ((ChannelExec) channel).setCommand(command);

                channel.setInputStream(null);

                ((ChannelExec) channel).setErrStream(System.err);

                InputStream in = channel.getInputStream();

                channel.connect();
                byte[] tmp = new byte[1024];
                while (true) {
                    while (in.available() > 0) {
                        int i = in.read(tmp, 0, 1024);
                        if (i < 0)
                            break;
                        results.add(new String(tmp, 0, i));
                        System.out.print(new String(tmp, 0, i));
                    }
                    if (channel.isClosed()) {
                        //System.out.println("exit-status: "
                        //      + channel.getExitStatus());
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (Exception ee) {
                        ee.printStackTrace();
                    }
                }
                channel.disconnect();
                session.disconnect();
            } catch (Exception e) {
                System.out.println(e);
            }
            setCommandResults(results);
            System.out.println("finished running");
        }
    }
}

私のjunitテスト内で:

@Test
    public void testRemoteExecution() {

        remoteCommand = (RemoteCommand) applicationContext.getBean("remoteCommand");
        remoteCommand.getCommandResults("scripts/something.pl xxx.xxx.xxx.xxx");

            //List<String> results = remoteCommand.getCommandResults("scripts/something.pl xxx.xxx.xxx.xxx");
        //for (String line : results) {
        //  System.out.println(line.trim());
        //}
    }

私のapplicationContext.xmlファイル:

    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
       <property name="corePoolSize" value="5" />
       <property name="maxPoolSize" value="10" />
       <property name="queueCapacity" value="25" />
    </bean>        

<!-- ******************** -->
<!--      Utilities       -->
<!-- ******************** -->

     <bean name="remoteCommand" class="com.xxx.ncc.sonet.utilities.RemoteCommand" scope="prototype">
        <description>Remote Command</description>
        <constructor-arg><value>${remote.user}</value></constructor-arg>
        <constructor-arg><value>${remote.Host}</value></constructor-arg>
        <constructor-arg><value>${remote.password}</value></constructor-arg>
        <constructor-arg ref="taskExecutor" />
    </bean> 

Run()メソッドの最初のprintlnまで取得します。その後、テストはエラーなしで正常に終了します。そのルーチンの最後にある2番目のprintlnに到達することはありません。私はこのスレッド here を見てきました。これは非常に便利でしたが、Spring固有の方法で実装されていませんでした。簡単なものが欠けているか、Railsここで完全に実行できませんでした。きっと助けになります。

21
Bill
public List<String> getCommandResults(String command) {
    FutureTask task = new FutureTask(new CommandTask(command))
    taskExecutor.execute(task);

    return task.get(); //or task.get(); return commandResults; - but it not a good practice
}
13
Igor Artamonov

TaskExecutorインターフェースは、タスクがいつ終了してもかまわない場合に使用する、ファイアアンドフォーゲットインターフェースです。これは、Springが提供する最も単純な非同期抽象化です。

ただし、拡張インターフェース AsyncTaskExecutor があり、Futureを返すsubmit()メソッドなど、追加のメソッドを提供して、結果を待ちます。

Springは ThreadPoolTaskExecutor クラスを提供し、TaskExecutorAsyncTaskExecutorの両方を実装します。

あなたの特定のケースでは、RunnableCallableとして再実装し、Callable.call()メソッドからcommandResultsを返します。次に、getCommandResultsメソッドを次のように再実装できます。

_public List<String> getCommandResults(String command) {
   Future<List<String>> futureResults = taskExecutor.submit(new CommandTask(command));
   return futureResults.get();
}
_

このメソッドはタスクを非同期に送信し、タスクが完了するのを待ってから、Callable.call()メソッドから返された結果を返します。これにより、commandResultsフィールドを削除することもできます。

48
skaffman