4

我在数据库中有一个提要列表,用于从 FTP 服务器下载 XML 文件,然后对其进行解析。该脚本被捆绑到一个 jar 文件中,该文件每天使用 Windows 任务计划程序运行。有时,请求会在抓取某个 xml 文件时被拖走。到目前为止,它在 2 周内发生了大约 3 次,我看不到真正的模式。

当它出现问题时,我转到运行它的计算机,我看到命令窗口打开,并且在 xml 完全下载之前停止。如果我关闭命令窗口并手动运行任务,一切都会正常工作。

我用来下载 xml 文件的代码是:

private void loadFTPFile(String host, String username, String password, String filename, String localFilename){
        System.out.println(localFilename);
        FTPClient client = new FTPClient();
        FileOutputStream fos = null;

        try {
            client.connect(host);
            client.login(username, password);
            String localFilenameOutput = createFile(assetsPath + localFilename);
            fos = new FileOutputStream(localFilenameOutput);
            client.retrieveFile(filename, fos);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) 
                    fos.close();
                client.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

这个函数在循环中被调用,当它失败时,一切都会停止,脚本不会进入下一个提要。

我不确定发生了什么,可能连接丢失,但我认为如果发生这种情况,try/catch 会捕获。我不确定超时是否可以解决问题或需要使用线程(但我从未使用过线程)

谁能指出我正确的方向为什么会发生这种情况以及我可以做些什么来解决这个问题

4

3 回答 3

2

更新 - 为数据连接设置超时

由于最后一个文件只是部分下载,并且给出了 的来源FTPClient.retrieveFile(),我认为这可能是服务器端的问题(导致它挂起,甚至死亡的东西 -谁知道)。显然一个人无法修复服务器,甚至无法知道那里发生了什么,无论如何我建议添加一个超时setDataTimeout(int)并捕获可能SocketTimeoutException单独登录到不同的地方,并可能发送给 FTP 服务器管理员(连同时间信息当它发生时),以便他们可以合并日志并查看问题所在。

旧答案

我没有注意到您为每个文件都连接和登录,所以以下只是一个优化,不关闭控制连接并成功注销,但它不应该解决问题。

您可以在调试模式下启动 JVM 并在挂起时附加一个调试器,无论如何根据这个答案这个线程它可能是网络设备设备(路由器)上的超时问题。来自FTPClient Javadoc

在文件传输过程中,数据连接处于忙碌状态,而控制连接处于空闲状态。FTP 服务器知道控制连接正在使用中,因此不会因为缺乏活动而关闭它,但是网络路由器要知道控制连接和数据连接是相互关联的要困难得多。一些路由器可能会将控制连接视为空闲,如果数据连接上的传输时间超过路由器允许的空闲时间,则将其断开。

对此的一种解决方案是通过控制连接发送安全命令(即 NOOP)以重置路由器的空闲计时器。启用如下:

ftpClient.setControlKeepAliveTimeout(300); // set timeout to 5 minutes
于 2012-08-22T18:57:39.417 回答
1

你检查任何调用的返回状态还是代码?

有一个调用 completePendingCommand() 必须在 occassion 时使用。这可能是需要调查的事情。

此外,您不会看到 IO 异常,我相信它会被重新打包为 CopyStreamException

您可能还希望将返回值更改为布尔值,因为您捕获了异常,至少调用循环将知道转移是否发生。

private boolean loadFTPFile(String host, String username, String password, String filename, String localFilename){
    System.out.println(localFilename);
    FTPClient client = new FTPClient();
    FileOutputStream fos = null;

    try {
        client.connect(host);

        int reply = client.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)){
            client.disconnect();
            System.err.println("FTP server refused connection.");
            return false;
        }


        if (!client.login(username, password)){
            client.logout();
            return false;
        }

        String localFilenameOutput = createFile(assetsPath + localFilename);
        fos = new FileOutputStream(localFilenameOutput);
        boolean result = client.retrieveFile(filename, fos);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

        if (result){
            System.out.println("\tFile Transfer Completed Successfully at: " + sdf.format(Calendar.getInstance().getTime()));

            // ftp.completePendingCommand();
        }
        else {
            System.out.println("\tFile Transfer Failed at: " + sdf.format(Calendar.getInstance().getTime()));
        }

    return result;
    }catch (CopyStreamException cse){
        System.err.println("\n\tFile Transfer Failed at: " + sdf.format(Calendar.getInstance().getTime()));
        System.err.println("Error Occurred Retrieving File from Remote System, aborting...\n");
        cse.printStackTrace(System.err);
        System.err.println("\n\nIOException Stack Trace that Caused the Error:\n");
        cse.getIOException().printStackTrace(System.err);
        return false;
    }catch (Exception e){
        System.err.println("\tFile Transfer Failed at: " + sdf.format(Calendar.getInstance().getTime()));
        System.out.println("Error Occurred Retrieving File from Remote System, aborting...");
        e.printStackTrace(System.err);
        return false;
    } finally {
        try {
            if (fos != null) 
                fos.close();
            client.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
于 2012-08-22T18:50:34.787 回答
0

这不是线程问题。很可能它是由循环中的某些东西引起的,因为该代码看起来应该清理得很好。也就是说,对于测试,您可能需要添加

catch (Exception e) {
    e.printStackTrace();
}

IOExceptioncatch 子句之后。可能会引发另一个异常。

另一件事,如果您一次从数据库结果集中提取一个结果并进行 FTP 获取,那可能是个问题。除非结果都被 JDBC 调用一次带回,否则也可能超时。并非所有数据库查询实际上都会一次将整个结果集返回给客户端。

于 2012-08-22T18:43:20.407 回答