23

次の Apache Commons Net FTP コードを使用して、FTP サーバーに接続し、いくつかのディレクトリでファイルをポーリングし、ファイルが見つかった場合はそれらをローカル マシンに取得します。

try {
logger.trace("Attempting to connect to server...");

// Connect to server
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(20000);
ftpClient.connect("my-server-host-name");
ftpClient.login("myUser", "myPswd");
ftpClient.changeWorkingDirectory("/loadables/");

// Check for failed connection
if(!FTPReply.isPositiveCompletion(ftpClient.getReplyCode()))
{
    ftpClient.disconnect();
    throw new FTPConnectionClosedException("Unable to connect to FTP server.");
}

// Log success msg
logger.trace("...connection was successful.");

// Change to the loadables/ directory where we poll for files
ftpClient.changeWorkingDirectory("/loadables/");    

// Indicate we're about to poll
logger.trace("About to check loadables/ for files...");

// Poll for files.
FTPFile[] filesList = oFTP.listFiles();
for(FTPFile tmpFile : filesList)
{
    if(tmpFile.isDirectory())
        continue;

    FileOutputStream fileOut = new FileOutputStream(new File("tmp"));
    ftpClient.retrieveFile(tmpFile.getName(), fileOut);
    // ... Doing a bunch of things with output stream
    // to copy the contents of the file down to the local
    // machine. Ommitted for brevity but I assure you this
    // works (except when the WAR decides to hang).
    //
    // This was used because FTPClient doesn't appear to GET
    // whole copies of the files, only FTPFiles which seem like
    // file metadata...
}

// Indicate file fetch completed.
logger.trace("File fetch completed.");

// Disconnect and finish.
if(ftpClient.isConnected())
    ftpClient.disconnect();

logger.trace("Poll completed.");
} catch(Throwable t) {
    logger.trace("Error: " + t.getMessage());
}

これは毎分、毎分実行されるようにスケジュールされています。Tomcat (7.0.19) にデプロイすると、このコードは問題なく読み込まれ、問題なく動作し始めます。ただし、毎回、ある時点でハングしているように見えます。つまり、次のことを意味します。

  • ヒープ ダンプが存在しない
  • Tomcat はまだ実行中です (その pid を確認でき、Web マネージャー アプリにログインできます)
  • マネージャー アプリ内で、WAR がまだ実行中または開始されていることがわかります
  • catalina.out私のアプリケーション固有のログには、例外がスローされた兆候はありません

したがって、JVM は引き続き実行されます。Tomcat はまだ実行されており、展開した WAR はまだ実行されていますが、ハングしているだけです。2 時間実行してハングすることもあります。また、何日も実行されてからハングすることもあります。しかし、ハングする場合は、読み取り行About to check loadables/ for files...(ログに表示されます) と読み取り行File fetch completed.(表示されません) の間で停止します。

これは、ファイルの実際のポーリング/フェッチ中にハングが発生したことを示しています。これは、FTPClient のデッドロックに関係することを見つけることができたこの質問と同じ方向性を示しています。これは、これらが同じ問題であるかどうか疑問に思っています (もしそうなら、喜んでこの質問を削除します! )。ただし、それらが同じであるとは思いませ(ログに同じ例外が表示されません)。

同僚は、それが「パッシブ」対「アクティブ」な FTP である可能性があると述べました。違いがよくわからないので、FTPClientフィールドなどに少し混乱しており、ACTIVE_REMOTE_DATA_CONNECTION_MODESOPASSIVE_REMOTE_DATA_CONNECTION_MODEがそれを潜在的な問題と考えていることを知りませんでした。

ここでは最後の手段として s をキャッチしているので、何か問題が発生した場合はログに何かThrowableが表示されると予想していました。エルゴ、これは明確なハングの問題だと思います。

何か案は?残念ながら、ここでの FTP の内部については、確固たる診断を下すのに十分な知識がありません。これはサーバー側のものでしょうか?FTP サーバーに関連していますか?

4

4 に答える 4

32

これには多くのことが考えられますが、友人の提案は価値があります。

ftpClient.enterLocalPassiveMode();それが役立つかどうかを確認してください 。

また、接続がそこに残らないように、切断をfinallyブロックに入れることをお勧めします。

于 2012-03-14T17:30:26.447 に答える
21

昨日は眠れませんでしたが、問題は解決したと思います。

FTPClient.setBufferSize(); でバッファ サイズを増やすことができます。

   /**
 * Download encrypted and configuration files.
 * 
 * @throws SocketException
 * @throws IOException
 */
public void downloadDataFiles(String destDir) throws SocketException,
        IOException {

    String filename;
    this.ftpClient.connect(ftpServer);
    this.ftpClient.login(ftpUser, ftpPass);

    /* CHECK NEXT 4 Methods (included the commented) 
    *  they were very useful for me!
    *  and icreases the buffer apparently solve the problem!!
    */
    //  ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
    log.debug("Buffer Size:" + ftpClient.getBufferSize());
    this.ftpClient.setBufferSize(1024 * 1024);
    log.debug("Buffer Size:" + ftpClient.getBufferSize());


    /*  
     *  get Files to download
     */
    this.ftpClient.enterLocalPassiveMode();
    this.ftpClient.setAutodetectUTF8(true);
            //this.ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    this.ftpClient.enterLocalPassiveMode();
    FTPFile[] ftpFiles = ftpClient
            .listFiles(DefaultValuesGenerator.LINPAC_ENC_DIRPATH);

    /*
     * Download files
     */
    for (FTPFile ftpFile : ftpFiles) {

        // Check if FTPFile is a regular file           
        if (ftpFile.getType() == FTPFile.FILE_TYPE) {
            try{

            filename = ftpFile.getName();

            // Download file from FTP server and save
            fos = new FileOutputStream(destDir + filename);

            //I don't know what useful are these methods in this step
            // I just put it for try
            this.ftpClient.enterLocalPassiveMode();
            this.ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            this.ftpClient.setAutodetectUTF8(true);
            this.ftpClient.enterLocalPassiveMode();

            ftpClient.retrieveFile(
                    DefaultValuesGenerator.LINPAC_ENC_DIRPATH + filename,
                    fos
                    );

            }finally{
                fos.flush();
                fos.close();                }
        }
    }
    if (fos != null) {
        fos.close();
    }
}

このコードが誰かの役に立つことを願っています!

于 2013-05-08T16:48:58.957 に答える
4

s.listFiles を呼び出して、「ハング」して最終的に失敗することなく転送するには、ログイン後に次を含める必要がありました。

s.login(username, password);
s.execPBSZ(0);
s.execPROT("P");
于 2015-08-21T19:30:31.843 に答える