URLConnection的Java应用程序导致“打开的文件过多”

时间:2011-11-11 09:30:26

标签: java linux handle lsof

我写了一小段java程序如下:

package com.ny.utils.pub;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class NetWriter {
private static String link = "http://xxx.yyyyyy.com:4444";

public String getLink() {
    return link;
}

public static void setLink(String link) {
    NetWriter.link = link;
}

private static HttpURLConnection conn = null;
private static BufferedReader bufReader = null;
private static InputStreamReader isReader = null;
private static OutputStreamWriter osw = null;
private static URL url = null;
static {
    try {
        url = new URL(link);
    } catch(MalformedURLException e) {
    }
}

public static void write(String msg) {
    long threadId = Thread.currentThread().getId();
    System.out.println("--Insert>{" + threadId + "}:" + msg);
    try {
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", 
              "application/x-www-form-urlencoded");
        conn.setRequestProperty("Content-Language", "en-US");
        conn.setUseCaches(false);
        conn.setDoOutput(true);
        conn.setReadTimeout(5000);
        conn.setConnectTimeout(5000);

        osw = new OutputStreamWriter(conn.getOutputStream());
        osw.write(msg);
        osw.flush();
        osw.close();

        if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
            System.err.println("Server not return HTTP_OK status");
        } else {
            System.out.println(" request: " + msg);
            isReader = new InputStreamReader(conn.getInputStream());
            bufReader = new BufferedReader(isReader);
            String rep = bufReader.readLine();
            if (conn.getResponseCode() == 200) {
                System.out.println("Post data OK to " + link);
            }
            System.out.println(" response: " + rep);
        }
    } catch(IOException e) {
        System.err.println("Post data error: " + link + " "
                + e.getMessage());
        e.printStackTrace();
    }
}
}

当我编写另一个程序来调用此类中的方法时,将导致“打开太多文件”,然后操作系统将拒绝用户登录。被调用的脚本如下:

try{        
    NetWriter.write(new String(content, "utf-8")); 
}catch(Exception e){
    logger.error(e.getMessage());
    e.printStackTrace();
    }
}

当问题再次出现时,我发现手柄占用正在增加。 以下是我执行命令“lsof -p PROGRAM_PID”

的消息
java    27439 root   66u  unix 0xffff8103473fb6c0             10151765 socket
java    27439 root   67u  unix 0xffff8103473fb6c0             10151765 socket
java    27439 root   68u  unix 0xffff8103473fb6c0             10151765 socket
java    27439 root   69r  FIFO                0,6             10151917 pipe
java    27439 root   70w  FIFO                0,6             10151917 pipe
java    27439 root   71r  0000               0,11          0  10151918 eventpoll
java    27439 root   72r  FIFO                0,6             10151919 pipe
java    27439 root   73w  FIFO                0,6             10151919 pipe
java    27439 root   74r  0000               0,11          0  10151920 eventpoll
java    27439 root   75u  unix 0xffff8103473fb6c0             10151765 socket
java    27439 root   76u  unix 0xffff8103473fb6c0             10151765 socket
java    27439 root   77r  FIFO                0,6             10152042 pipe
java    27439 root   78w  FIFO                0,6             10152042 pipe
java    27439 root   79r  0000               0,11          0  10152043 eventpoll
java    27439 root   80r  FIFO                0,6             10152044 pipe
java    27439 root   81w  FIFO                0,6             10152044 pipe
java    27439 root   82r  0000               0,11          0  10152045 eventpoll
java    27439 root   83u  unix 0xffff8103473fb6c0             10151765 socket
java    27439 root   84r  FIFO                0,6             10154168 pipe
java    27439 root   85w  FIFO                0,6             10154168 pipe
java    27439 root   86r  0000               0,11          0  10154169 eventpoll
java    27439 root   87r  FIFO                0,6             10154170 pipe

句柄数(管道插槽事件池)最多可达数千个。

我尝试了很多方法来避免这种情况,但失败了。 有谁能告诉我上述程序中的缺陷?

3 个答案:

答案 0 :(得分:4)

您不要关闭输入阅读器。它应该关闭。

作为一般规则,您应该关闭finally块中的资源。

在这种情况下,您应该在finally块中关闭输入和输出阅读器。

答案 1 :(得分:1)

如果您还没有找到解决方案,请查看以下内容:

Android - HttpUrlConnection is not closing. Eventually results to SocketException

这与" Keep-Alive" Jellybean中的连接。希望有所帮助

答案 2 :(得分:0)

尝试使用OkHttp

我为此花费了数小时/天/周的时间,做了所有您想设置的小的读取/连接超时并使用finally块来关闭连接,输入流,输出流等的操作。但是我们从来没有找到了可行的解决方案。 HttpsUrlConnection似乎存在某种缺陷。

因此,我们尝试用OkHttp替代HttpsUrlConnection和瞧!它开箱即用。

因此,如果您对此感到挣扎,并且很难解决它,建议您也尝试使用OkHttp。

以下是基本知识:

一旦添加了Maven依赖项,就可以执行以下操作来下载文件:

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

OutputStream output = null;

try {
  Request request   = new Request.Builder().url( download_url ).build();
  Response response = okHttpClient.newCall( request ).execute();

  if ( !response.isSuccessful() ) {
    throw new FileNotFoundException();
  }

  output = new FileOutputStream( output_path );

  output.write( response.body().bytes() );
}
finally {
  // Ensure streams are closed, even if there's an exception.
  if ( output != null ) output.flush();
  if ( output != null ) output.close();
}

切换到OkHttp会立即解决我们泄漏的文件描述符问题,因此即使您被卡住了也值得尝试,即使以增加另一个库依赖项为代价。