Java Commons Net FTPClient与Mule无限期挂起
我在Mule ESB FTP传输中遇到了一个问题:轮询时,运行客户端的线程将无限期挂起,而不会抛出错误。这会导致FTP轮询完全停止。Mule使用ApacheCommonsNetFTPClient 进一步查看代码,我认为这是由于未设置FTPClient的SocketTimeout造成的,有时在从FTPClient的套接字读取行时会导致无限挂起 当问题发生时,我们可以清楚地看到使用jstack检索的这些堆栈中的问题。__getReply()函数似乎是与问题更直接的联系 创建新FTPClient时,此函数挂起connect()调用:Java Commons Net FTPClient与Mule无限期挂起,java,sockets,ftp,mule,freeze,Java,Sockets,Ftp,Mule,Freeze,我在Mule ESB FTP传输中遇到了一个问题:轮询时,运行客户端的线程将无限期挂起,而不会抛出错误。这会导致FTP轮询完全停止。Mule使用ApacheCommonsNetFTPClient 进一步查看代码,我认为这是由于未设置FTPClient的SocketTimeout造成的,有时在从FTPClient的套接字读取行时会导致无限挂起 当问题发生时,我们可以清楚地看到使用jstack检索的这些堆栈中的问题。__getReply()函数似乎是与问题更直接的联系 创建新FTPClient时,此
receiver.172 prio=10 tid=0x00007f23e43c8800 nid=0x2d5 runnable [0x00007f24c32f1000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x00000007817a9578> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
- locked <0x00000007817a9578> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP._connectAction_(FTP.java:364)
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:540)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:178)
at org.mule.transport.ftp.FtpConnectionFactory.makeObject(FtpConnectionFactory.java:33)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1188)
at org.mule.transport.ftp.FtpConnector.getFtp(FtpConnector.java:172)
at org.mule.transport.ftp.FtpConnector.createFtpClient(FtpConnector.java:637)
at org.mule.transport.ftp.FtpMessageReceiver.listFiles(FtpMessageReceiver.java:134)
at org.mule.transport.ftp.FtpMessageReceiver.poll(FtpMessageReceiver.java:94)
at org.mule.transport.AbstractPollingMessageReceiver.performPoll(AbstractPollingMessageReceiver.java:216)
at org.mule.transport.PollingReceiverWorker.poll(PollingReceiverWorker.java:80)
at org.mule.transport.PollingReceiverWorker.run(PollingReceiverWorker.java:49)
at org.mule.transport.TrackingWorkManager$TrackeableWork.run(TrackingWorkManager.java:267)
at org.mule.work.WorkerContext.run(WorkerContext.java:286)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- <0x00000007817a3540> (a java.util.concurrent.ThreadPoolExecutor$Worker)
它使用FTPClient()构造函数,它本身使用SocketClient,超时为0,在创建套接字时定义
public SocketClient()
{
...
_timeout_ = 0;
...
}
然后我们调用connec(),它调用uConnectAction()
在SocketClient中:
protected void _connectAction_() throws IOException
{
...
_socket_.setSoTimeout(_timeout_);
...
}
在FTP中,一个新的读卡器与我们的永久套接字关联:
protected _connectAction_(){
...
_controlInput_ =
new BufferedReader(new InputStreamReader(_socket_.getInputStream(),
getControlEncoding()));
...
}
然后,在调用uu getReply()函数时,我们将此读取器与永久套接字一起使用:
private void __getReply() throws IOException
{
...
String line = _controlInput_.readLine();
...
}
很抱歉发了这么长的帖子,但我认为这需要正确的解释。解决方案可能是在connect()之后调用setSoTimeout(),以定义套接字超时
默认超时似乎不是一个可接受的解决方案,因为每个用户可能有不同的需求,并且默认超时在任何情况下都不合适
最后,这提出了两个问题:
在理想情况下,应该不需要超时,但在您的情况下似乎是这样 你的描述很全面,你有没有考虑过提出一个建议
为了解决这个问题,我建议首先在“高级”选项卡中使用“响应超时”。如果这不起作用,我会使用a,从那里你应该能够覆盖接收器 在我之前的两个案例中,我都使用了复制错误,并且我能够使用FtpConnectionFactory来解决这个问题
public class SafeFtpConnectionFactory extends FtpConnectionFactory{
//define a default timeout
public static int defaultTimeout = 60000;
public static synchronized int getDefaultTimeout() {
return defaultTimeout;
}
public static synchronized void setDefaultTimeout(int defaultTimeout) {
SafeFtpConnectionFactory.defaultTimeout = defaultTimeout;
}
public SafeFtpConnectionFactory(EndpointURI uri) {
super(uri);
}
@Override
protected FTPClient createFtpClient() {
FTPClient client = super.createFtpClient();
//Define the default timeout here, which will be used by the socket by default,
//instead of the 0 timeout hanging indefinitely
client.setDefaultTimeout(getDefaultTimeout());
return client;
}
}
然后将其连接到我的连接器:
<ftp:connector name="archivingFtpConnector" doc:name="FTP"
pollingFrequency="${frequency}"
validateConnections="true"
connectionFactoryClass="my.comp.SafeFtpConnectionFactory">
<reconnect frequency="${reconnection.frequency}" count="${reconnection.attempt}"/>
</ftp:connector>
否则,在connect()或pasv()上的尝试将无限期挂起,而没有服务器响应。我使用模拟FTP复制了这个精确的行为
注意:我使用了setDefaultTimeout(),因为它似乎是connect()和connectAction()使用的变量(来自SocketClient源代码):
编辑:对于感兴趣的人,这里是模拟FTP的测试代码,用于复制从不应答服务器。然而,无限循环远不是好的实践。它应该被类似于sleep的东西所取代,其中封装的测试类预期SocketTimeout异常,并确保在给定超时后失败
private static final int CONTROL_PORT = 2121;
public void startStubFtpServer(){
FakeFtpServer fakeFtpServer = new FakeFtpServer();
//define the command which should never be answered
fakeFtpServer.setCommandHandler(CommandNames.PASV, new EverlastingCommandHandler());
//fakeFtpServer.setCommandHandler(CommandNames.CONNECT, new EverlastingConnectCommandHandler());
//or any other command...
//server config
...
//start server
fakeFtpServer.setServerControlPort(CONTROL_PORT);
fakeFtpServer.start();
...
}
//will cause any command received to never have an answer
public class EverlastingConnectCommandHandler extends org.mockftpserver.core.command.AbstractStaticReplyCommandHandler{
@Override
protected void handleCommand(Command cmd, Session session, InvocationRecord rec) throws Exception {
while(true){
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
//TODO
}
}
}
}
public class EverlastingCommandHandler extends AbstractFakeCommandHandler {
@Override
protected void handle(Command cmd, Session session) {
while(true){
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
//TODO
}
}
}
};
你好,维克多,我昨天也试图提出一个错误,但注册页面目前似乎不可用(给我一个404)。如果问题仍然存在,我会尝试联系管理员。ResponseTimeout设置为10000,但这并不能阻止该问题。我目前正在使用复制,并尝试自定义FtpConnectionFactory。覆盖接收器似乎也是一个好主意,谢谢!我尝试将服务覆盖与自定义messageReceiver(FtpMessageReceiver)一起使用,似乎FTP客户端的创建是在这个类的多个位置完成的。这将要求创建新客户机的任何函数都要通过每次设置超时的调用重新编写。但是,由于每个客户端创建调用都源于FtpConnectionFactory,因此我认为应该可以替代它。
<ftp:connector name="archivingFtpConnector" doc:name="FTP"
pollingFrequency="${frequency}"
validateConnections="true"
connectionFactoryClass="my.comp.SafeFtpConnectionFactory">
<reconnect frequency="${reconnection.frequency}" count="${reconnection.attempt}"/>
</ftp:connector>
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP._connectAction_(FTP.java:364)
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:540)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:178)
at org.mule.transport.ftp.FtpConnectionFactory.makeObject(FtpConnectionFactory.java:33)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1188)
at org.mule.transport.ftp.FtpConnector.getFtp(FtpConnector.java:172)
at org.mule.transport.ftp.FtpConnector.createFtpClient(FtpConnector.java:637)
...
public abstract class SocketClient
{
...
protected void _connectAction_() throws IOException
{
...
_socket_.setSoTimeout(_timeout_);
...
}
...
public void setDefaultTimeout(int timeout)
{
_timeout_ = timeout;
}
...
}
private static final int CONTROL_PORT = 2121;
public void startStubFtpServer(){
FakeFtpServer fakeFtpServer = new FakeFtpServer();
//define the command which should never be answered
fakeFtpServer.setCommandHandler(CommandNames.PASV, new EverlastingCommandHandler());
//fakeFtpServer.setCommandHandler(CommandNames.CONNECT, new EverlastingConnectCommandHandler());
//or any other command...
//server config
...
//start server
fakeFtpServer.setServerControlPort(CONTROL_PORT);
fakeFtpServer.start();
...
}
//will cause any command received to never have an answer
public class EverlastingConnectCommandHandler extends org.mockftpserver.core.command.AbstractStaticReplyCommandHandler{
@Override
protected void handleCommand(Command cmd, Session session, InvocationRecord rec) throws Exception {
while(true){
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
//TODO
}
}
}
}
public class EverlastingCommandHandler extends AbstractFakeCommandHandler {
@Override
protected void handle(Command cmd, Session session) {
while(true){
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
//TODO
}
}
}
};