わかりました、ここでは単純な質問の可能性があります。複数のネットワークデバイスにログインし、それぞれにコマンドを実行して結果を収集する必要があるサービスがあります。速度を上げるために、各デバイスの情報を順番に収集するのではなく、それらすべてに同時にアクセスして、完了後に結果を消費する必要があります。
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ここで完全に実行できませんでした。きっと助けになります。
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
}
TaskExecutor
インターフェースは、タスクがいつ終了してもかまわない場合に使用する、ファイアアンドフォーゲットインターフェースです。これは、Springが提供する最も単純な非同期抽象化です。
ただし、拡張インターフェース AsyncTaskExecutor
があり、Future
を返すsubmit()
メソッドなど、追加のメソッドを提供して、結果を待ちます。
Springは ThreadPoolTaskExecutor
クラスを提供し、TaskExecutor
とAsyncTaskExecutor
の両方を実装します。
あなたの特定のケースでは、Runnable
をCallable
として再実装し、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
フィールドを削除することもできます。