Tomcat 7 Servlet产生一个线程

时间:2014-09-19 10:35:06

标签: java multithreading tomcat servlets threadpool

我正在编写一个我想用来执行FTP任务(下载)的Web应用程序

我在Tomcat中安装了Apache FTPS服务器,并准备好Java客户端来启动传输。

Servlet将调用客户端。

例如:

http://laptop:8080/MyServlet?action=download&from=desktop&file=C:/home/fred/file.xml 

告诉笔记本电脑上的实例从我的桌面下载 file.xml

修改: 道歉,我从来没有说清楚。

此过程的两端都有一台FTP服务器。 1在我的远程笔记本电脑上,1在我的本地桌面上。所以简而言之,我向远程端的Servlet提交了一个FTP'get'请求。然后,Servlet启动FTP进程以将文件拉过来。

我的Servlet已设置为接收GET参数并完成工作。

如果文件很大,那么每个请求都需要很长时间才能完成。我希望尽快释放Servlet资源。

理想情况下,我希望发生以下事情:

  1. 用户将URL发送到Servlet
  2. Servlet消化URL并找出哪些文件以及来自哪里...
  3. 将信息传递给线程的Servlet
  4. Servlet将返回“正在进行中”消息
  5. 请求完成
  6. 线程仍然在后台下载文件
  7. 此时我并不太关心Servlet对线程成功的了解,我只是需要它来启动它而忘记它。对于任何问题,FTP进程将在其他位置单独记录。

    我感兴趣的是在WebApp中创建Threadpool并从那里获取线程的概念,但是我发现的所有示例都很旧,并不能满足我的理解水平。

    在StackOverflow上有一些类似的问题,这与我要求的最相似,但它只暗示了我之前不知道的ExecutorService。我如何在WebApp中进行设置? What is recommended way for spawning threads from a servlet in Tomcat

    有关信息, 我研究了这个问题,发现了很多不完整的例子,需要比我现在更好的理解,或暗示需要什么。 我读过的很多例子都是几年前的,没有最新的。我希望可能会有一个神奇的单行代码去做我需要的一切(可疑)在去年左右出现:) 我是Java的线程概念的新手,我理解一般的线程,所以感谢你能给我提供的任何帮助。

    特雷弗

1 个答案:

答案 0 :(得分:0)

我不确定我是否真的明白你想要的东西......

client                                      server
send request (via HTTP) and wait for
         HTTP response
                                            analyse request and find file to send
                                            ... (processing)
                                            send HTTP response (1) with ?
opens FTP connection (could not open it before)
                                            receive FTP request (command connection)
                                            send file (data connection)
file is received and saved locally

如果客户端是浏览器,则响应(1)应该足够重定向到ftp://host/path/to/file之类的URL,因为所有主流浏览器都知道FTP协议并能够使用它下载文件。

问题不在服务器端,你可以很容易地产生一个可以作为FTP客户端的线程或者(可能更难)作为FTP服务器,但我无法想象比客户端的重定向更好:客户端已打开HTTP连接不能用于FTP传输,必须为FTP请求打开新连接。由于它是一个新连接,您希望如何通过上一步启动的线程处理它?在FTP中没有会话概念,也没有简单的方法来识别正确的请求。

根据评论编辑:

好的,我似乎只是想在请求完成后在服务器上进行 deferred 处理。你有两种方法:

  • 按照标签的建议,使用工作线程来完成工作。您的servlet是纯Java,您可以像在任何其他Java应用程序中一样创建一个线程。如果您有兴趣稍后获得延迟处理的结果,您可以将该会话(或简称为会话属性)的引用提供给它将能够提升其进度和/或完成状态的线程。这需要更多的锅炉板代码,但保证可以工作(下面的例子)
  • 您可以在servlet返回之前关闭HTTP连接。根据官方servlet规范,它没有明确保证,但我发现它至少在tomcat 7中有用。你会在另一篇文章Servlet - close connection but not method
  • 上找到更多相关信息。

使用简单线程并在会话中存储状态的示例:

public class ThreadedServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest hsr, HttpServletResponse hsr1) throws ServletException, IOException {
        String fileName = null;
        // preliminary work ...
        Worker worker = new Worker(fileName);
        final HttpSession session = hsr.getSession();
        synchronized (session) {
            List<Status> statuses = (List<Status>) session.getAttribute("statuses");
            if (statuses == null) {
                statuses = new ArrayList<Status>();
            }
            statuses.add(new Status(fileName));
        }
        Thread thr = new Thread(worker);
        thr.start();
        // write the response either directly or by forwarding to a JSP
    }

    public static class Status implements Serializable {
        private String fileName;
        private WStatus status;

        public Status(String fileName) {
            this.fileName = fileName;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public WStatus getStatus() {
            return status;
        }

        public void setStatus(WStatus status) {
            this.status = status;
        }
    }

    public enum WStatus {
        STARTED,
        RUNNING,
        COMPLETED
    }

    private static class Worker implements Runnable {

        private String fileName;
        private Status status;

        public Worker(String fileName) {
            this.fileName = fileName;
        }

        @Override
        public void run() {
            status.setStatus(WStatus.RUNNING);
            // do your stuff ...
            status.setStatus(WStatus.COMPLETED);
        }
    }
}