복붙노트

[SPRING] 스프링 스레딩과 TaskExecutor를 사용하여 스레드가 완료되면 어떻게 알 수 있습니까?

SPRING

스프링 스레딩과 TaskExecutor를 사용하여 스레드가 완료되면 어떻게 알 수 있습니까?

좋아, 여기에 순진한 질문이있을 수있다. 여러 네트워크 장치에 로그인하고 각각에 명령을 실행하여 결과를 수집해야하는 서비스가 있습니다. 속도면에서 볼 때 각 장치의 정보를 순서대로 수집하는 대신, 동시에 모든 장치에 액세스하고 완료된 후에 결과를 사용해야합니다.

Spring 프레임 워크와 Jsch를 사용하면 각 장치를 올바르게 쉽게 쿼리 할 수 ​​있습니다. 내가 혼란을 겪고있는 곳에서 Bean을 재배치하여 TaskExecutor를 사용하여이 작업을 수행하려고한다. 내가 어떻게하는지 알 수없는 것은 쓰레드가 끝났을 때를 알 수있는 방법이다.

내가 지금까지 가지고있는 것은 이것이다.

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까지 도달한다. 그런 다음 테스트는 오류없이 깨끗하게 종료됩니다. 나는 그 루틴의 맨 아래에 두 번째 println을 가지지 않는다. 나는이 스레드를 여기서 보았는데, 이것은 매우 유용했지만 Spring 고유의 방식으로 구현되지 않았다. 나는 단순한 무언가를 놓치고 있거나 여기 레일을 완전히 벗어났다. 어떤 도움을 주셔서 감사합니다.

해결법

  1. ==============================

    1.

    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
    }
    
  2. ==============================

    2.TaskExecutor 인터페이스는 작업이 끝날 때 걱정하지 않을 때 사용할 수있는 화재 및 잊어 버린 인터페이스입니다. Spring이 제공하는 가장 단순한 비동기 추상화입니다.

    TaskExecutor 인터페이스는 작업이 끝날 때 걱정하지 않을 때 사용할 수있는 화재 및 잊어 버린 인터페이스입니다. Spring이 제공하는 가장 단순한 비동기 추상화입니다.

    그러나 향상된 인터페이스 인 AsyncTaskExecutor가 있으며 결과를 기다릴 수있는 Future를 반환하는 submit () 메서드를 비롯한 추가 메서드를 제공합니다.

    Spring은 TaskExecutor와 AsyncTaskExecutor를 구현하는 ThreadPoolTaskExecutor 클래스를 제공한다.

    귀하의 구체적인 경우, 나는 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 필드를 제거 할 수도 있습니다.

  3. from https://stackoverflow.com/questions/2269126/using-spring-threading-and-taskexecutor-how-do-i-know-when-a-thread-is-finished by cc-by-sa and MIT license