ftp-pool icon indicating copy to clipboard operation
ftp-pool copied to clipboard

linux环境:java.net.SocketException: Broken pipe (Write failed)

Open MarvinYu opened this issue 6 years ago • 27 comments

java.net.SocketException: Broken pipe (Write failed) at java.net.SocketOutputStream.socketWrite0(Native Method) at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) at java.net.SocketOutputStream.write(SocketOutputStream.java:153) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229) at java.io.BufferedWriter.flush(BufferedWriter.java:254) at org.apache.commons.net.ftp.FTP.__send(FTP.java:545) at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:519) at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:648) at org.apache.commons.net.ftp.FTP.cwd(FTP.java:868) at org.apache.commons.net.ftp.FTPClient.changeWorkingDirectory(FTPClient.java:1167) at com.yinghuo.contract.biz.core.util.ftp.FtpClientTemplate.uploadFile(FtpClientTemplate.java:56)

在linux 环境下容易出现session超时。需要保证keepAlive

MarvinYu avatar Nov 14 '18 03:11 MarvinYu

抱歉现在才看到,能否提供更详细的信息,比如是否在多线程环境下,或者文件是否过大,ftp服务器的配置之类的。 我改动了下代码,增加了配置ftp.client.keepAliveTimeout=30,不知道是否能解决你出现的问题,源码为: /** * Set the time to wait between sending control connection keepalive messages * when processing file upload or download. * * @param controlIdle the wait (in secs) between keepalive messages. Zero (or less) disables. * @since 3.0 * @see #setControlKeepAliveReplyTimeout(int) */ public void setControlKeepAliveTimeout(long controlIdle){ __controlKeepAliveTimeout = controlIdle * 1000; }

jayknoxqu avatar Nov 21 '18 15:11 jayknoxqu

not help

fortunelee avatar Nov 22 '18 02:11 fortunelee

谢谢,我重新尝试下;我配置了这个参数,没看你底层实现 @jayknoxqu

这是我的配置 ftpClient.setControlKeepAliveTimeout(ftpClientConfig.getControlKeepAliveTimeout());

应该是服务器端口被关闭了,目前没有其他办法。

MarvinYu avatar Nov 22 '18 04:11 MarvinYu

https://stackoverflow.com/questions/51730863/ftpclient-how-to-get-around-org-apache-commons-net-io-copystreamexception-ioexc 看了下,还没想到很好的办法 @jayknoxqu

MarvinYu avatar Feb 13 '19 02:02 MarvinYu

你不会是sftp吧?

jayknoxqu avatar Feb 13 '19 03:02 jayknoxqu

你不会是sftp吧?

还真不是,就是普通的FTP。我不使用池化的方法还行,但是一起用池化的;就 at org.apache.commons.net.ftp.FTPClient.changeWorkingDirectory(FTPClient.java:1167) 最后看到底层还是那个流 flush 异常

MarvinYu avatar Feb 13 '19 03:02 MarvinYu

"我不使用池化的方法还行,但是一起用池化的",能具体描述下么我这边没办法复现出来,是像测试类里那样使用么?

jayknoxqu avatar Feb 13 '19 05:02 jayknoxqu

"我不使用池化的方法还行,但是一起用池化的",能具体描述下么我这边没办法复现出来,是像测试类里那样使用么?

多线程一次性发送100张图片到FTP服务器,使用FTPClientPool就会出现了;如果是使用 new FTPClient不会出现这种情况

MarvinYu avatar Feb 13 '19 06:02 MarvinYu

你能贴下代码么? https://github.com/jayknoxqu/ftp-pool/blob/master/src/test/java/com/zhenjin/ftp/FtpClientPoolTest.java

jayknoxqu avatar Feb 13 '19 06:02 jayknoxqu

FtpClientPool是标记为不弃用的; 正确使用连接池的方式是使用commons-pool2 提供的

GenericObjectPool<FTPClient> ftpClientPool=new GenericObjectPool<>(ftpClientFactory);

方法创建连接池

jayknoxqu avatar Feb 13 '19 06:02 jayknoxqu

你能贴下代码么? https://github.com/jayknoxqu/ftp-pool/blob/master/src/test/java/com/zhenjin/ftp/FtpClientPoolTest.java

/**

  • @author marvinYu

  • 用于测试 ftpClientTemplate:uploadFile 在Linux环境下与windows下面是否有区别

  • @version : LinuxFTPUploadTestJob.java, v 0.1 2019-02-13 18:01 Exp $ */ @JobHandler(value = "linuxFTPUploadTestJob") @Service public class LinuxFTPUploadTestJob extends AbstractJobHandler { @Resource private FtpClientTemplate ftpClientTemplate;

    @Override public ReturnT<String> doExecute(String s) throws Exception { int linuxFtpUploadSize = Integer.parseInt(SysConfigUtil .getSysConfigByType("linux_ftp_upload_size"));

     for (int i = 0; i < linuxFtpUploadSize; i++) {
         Runnable runnable = () -> {
             File file = new File(
                 "/usr/local/test.properties");
             boolean uploadResult = ftpClientTemplate.uploadFile(file, "/upload");
             String threadName = Thread.currentThread().getName();
             System.out.println("Thread 1-" + threadName + ":" + uploadResult);
         };
         runnable.run();
         Thread testThread = new Thread(runnable);
    
         testThread.start();
     }
    
     return ReturnT.SUCCESS;
    

    } }

JobHandler(xxxJob调度) 在Linux环境下定时周期1分钟,就可以看到后台有该异常

MarvinYu avatar Feb 14 '19 02:02 MarvinYu

FtpClientPool是标记为不弃用的; 正确使用连接池的方式是使用commons-pool2 提供的

GenericObjectPool<FTPClient> ftpClientPool=new GenericObjectPool<>(ftpClientFactory);

方法创建连接池

这个没有问题,我看了common-pool的实现是一致的。

MarvinYu avatar Feb 14 '19 02:02 MarvinYu

@jayknoxqu

windows环境下,长期观察下。就可以看到这个 org.apache.commons.net.ftp.FTPConnectionClosedException: FTP response 421 received. Server closed connection. at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:388) at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:300) at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:523) at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:648) at org.apache.commons.net.ftp.FTP.cwd(FTP.java:868) at org.apache.commons.net.ftp.FTPClient.changeWorkingDirectory(FTPClient.java:1167) at com.yinghuo.fundgateway.util.ftp.FtpClientTemplate.uploadFile(FtpClientTemplate.java:51) at com.yinghuo.fundgateway.biz.task.job.temp.LinuxFTPUploadTestJob.lambda$doExecute$0(LinuxFTPUploadTestJob.java:41) at java.lang.Thread.run(Thread.java:745)

MarvinYu avatar Feb 14 '19 02:02 MarvinYu

public FtpClientTemplate(FtpClientFactory ftpClientFactory) { this.ftpClientPool = new GenericObjectPool<>(ftpClientFactory); this.ftpClientPool.setTestOnBorrow(true); this.ftpClientPool.setTestOnCreate(true); this.ftpClientPool.setTestOnReturn(true); this.ftpClientPool.setTestWhileIdle(true); this.ftpClientPool.setMinEvictableIdleTimeMillis(60000); }

加了这个之后,看到异常堆栈。校验到Connection已经异常了 @jayknoxqu 对比了下,目前这个按照common-pool实现还是有很多优化空间。可能要像C3P0那样对于GenericObjectPool 处理下更适用于生产环境

MarvinYu avatar Feb 14 '19 03:02 MarvinYu

six six six

fortunelee avatar Feb 14 '19 05:02 fortunelee

写的太假了,自己写都比你那个强

fortunelee avatar Feb 14 '19 05:02 fortunelee

写的太假了,自己写都比你那个强

写的挺好的,还需要考虑的点还有很多而已。这个目前还是一个待完善版本,类似于 http://commons.apache.org/proper/commons-pool/examples.html

MarvinYu avatar Feb 14 '19 06:02 MarvinYu

非常感谢你提出了这么多问题, FTP response 421 received这个问题我也检测出来了,感觉应该是保活的问题 GenericObjectPool确实是需要优化下

jayknoxqu avatar Feb 14 '19 06:02 jayknoxqu

应该的,互相学习。应该是保活机制不完善,连接池没有对于池中连接进行有效性处理以及外部服务,网络抖动都可能导致这些失效。

MarvinYu avatar Feb 14 '19 06:02 MarvinYu

写的太假了,自己写都比你那个强 @fortunelee 这个用于生产确实有问题,此项目主要是为了教学,让参与这个项目的人了解并熟悉连接池 ,通过这个项目你应该掌握: 1.连接池的基本方法和调用流程, 2.Queue对列的基本用法.有界对列,无界对列等相关概念, ...

jayknoxqu avatar Feb 14 '19 06:02 jayknoxqu

Redis 的客户端 Jedis也是通过GenericObjectPool来实现的

  public JedisPool(final String host) {
    URI uri = URI.create(host);
    if (JedisURIHelper.isValid(uri)) {
      String h = uri.getHost();
      int port = uri.getPort();
      String password = JedisURIHelper.getPassword(uri);
      int database = JedisURIHelper.getDBIndex(uri);
      boolean ssl = uri.getScheme().equals("rediss");
      this.internalPool = new GenericObjectPool<Jedis>(new JedisFactory(h, port,
          Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, password, database, null,
            ssl, null, null, null), new GenericObjectPoolConfig());
    } else {
      this.internalPool = new GenericObjectPool<Jedis>(new JedisFactory(host,
          Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null,
          Protocol.DEFAULT_DATABASE, null, false, null, null, null), new GenericObjectPoolConfig());
    }
  }

可看下它具体是怎么来实现的,

jayknoxqu avatar Feb 14 '19 07:02 jayknoxqu

@jayknoxqu 就你玩的花哨

fortunelee avatar Feb 22 '19 09:02 fortunelee

在检测到对象失效的时候,重新登录连接上ftp服务器,在放回对象池

hackerys avatar Jun 13 '19 02:06 hackerys

试下这几个参数,每次获取一个连接,返还一个连接都测试 一下是否是可用的状态 ,如果不是会自动删除并重建,然后获取下一个 pool.setTestOnBorrow(true); pool.setTestWhileIdle(true); pool.setTestOnReturn(true);

leyoliu1987 avatar Jan 16 '20 06:01 leyoliu1987

有更强的版本吗?从池中获取的client可能是断开的。导致程序异常。

xchd avatar May 27 '20 09:05 xchd

@fortunelee 楼主写的这个挺好的,思想不错,pool2这个思想的确值得学习。但是FTP这个协议的确很复杂,数据口和控制口是两个socket,而且还要根据具体的FTP server端去配置client。兄台你这个头像挺骚气啊

cookiespiggy avatar Mar 23 '21 07:03 cookiespiggy

Dear     email is ok!

cookiespiggy avatar Jan 24 '24 13:01 cookiespiggy