【问题标题】:java.net.SocketException: Socket Closed eventhough input and output streams are closed after read, writejava.net.SocketException: Socket Closed 即使输入和输出流在读、写后关闭
【发布时间】:2018-04-14 17:30:59
【问题描述】:

我正在运行多线程 Java 应用程序,其中以下方法将由许多线程(大约 300 个)并行执行。我只在线程数更多而不是线程数小于 50 时才看到此异常。 我在 stackoverflow 中阅读了几篇文章,并且代码看起来对所描述的帖子的抱怨。任何帮助,将不胜感激。提前致谢。

这是我的方法:

public static String invokeWebService(String request, String fwfmId, BatchVO batchVO){
    String responseString = "";
    //log the request string
    Logger_trace.info("Service request for input "+ fwfmId +":: "+CommonUtil.removeCrLf(request));
    try {
            URL url = new URL(batchVO.getWebServiceUrl());
            HttpURLConnection  urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("Content-type", "text/xml; charset=utf-8");

            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            urlConnection.setConnectTimeout(Integer.parseInt(batchVO.getConnectionTimeOut()));
            urlConnection.setReadTimeout(Integer.parseInt(batchVO.getReadTimeOut()));
            OutputStream outputStream = urlConnection.getOutputStream();
            outputStream.write(request.getBytes());
            outputStream.flush();

            //get response form input stream and convert into string
            InputStreamReader inputStreamReader = new InputStreamReader(urlConnection.getInputStream());
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = bufferedReader.readLine()) != null) {
                response.append(inputLine);
            }

            responseString = response.toString();

            //close resources
            bufferedReader.close();
            inputStreamReader.close();
            outputStream.close(); 

            //log the response string
            logger_trace.info("Service response for input "+ fwfmId +":: "+ CommonUtil.removeCrLf(responseString));

            //Update Database columns
            updateRecordInTable(request, responseString, fwfmId, batchVO.getDataSource(), batchVO.getBatchId());    

        } catch (SocketConnectException e) {
            logger_trace.error("WebserviceHandler.invokeWebService : SocketConnectException block : "+fwfmId +" ", e);
            updateRecordInTableWithTimeout(fwfmId, batchVO.getDataSource(), batchVO.getBatchId());
        } catch (SocketTimeoutException e) {
            logger_trace.error("WebserviceHandler.invokeWebService : SocketTimeoutException block : "+ fwfmId+" ", e);
            updateRecordInTableWithTimeout(fwfmId, batchVO.getDataSource(), batchVO.getBatchId());
        } catch (MalformedURLException e) {
            logger_trace.error("WebserviceHandler.invokeWebService : MalformedURLException block : ", e);
        } catch (IOException e) {
            logger_trace.error("WebserviceHandler.invokeWebService : IOException block : ", e);
        } catch(Exception e){
            logger_trace.error("WebserviceHandler.invokeWebService : General Exception block : ", e);
        }
        return responseString;
    }

例外:

 [ERROR] 2017-10-31 03:18:54,435 [main] TRACE -
 WebserviceHandler.invokeWebService : IOException block : 
 java.net.SocketException: Socket Closed
         at java.net.AbstractPlainSocketImpl.setOption(AbstractPlainSocketImpl.java:212)
 ~[?:1.8.0_131]
         at java.net.Socket.setTcpNoDelay(Socket.java:980) ~[?:1.8.0_131]
         at weblogic.net.http.HttpClient.openServer(HttpClient.java:411)
 ~[wlfullclient.jar:12.2.2.0.0]
         at weblogic.net.http.HttpClient.openServer(HttpClient.java:511)
 ~[wlfullclient.jar:12.2.2.0.0]
         at weblogic.net.http.HttpClient.New(HttpClient.java:313) ~[wlfullclient.jar:12.2.2.0.0]
         at weblogic.net.http.HttpURLConnection.getHttpClient(HttpURLConnection.java:314)
 ~[wlfullclient.jar:12.2.2.0.0]
         at weblogic.net.http.HttpURLConnection.getInputStream(HttpURLConnection.java:760)
 ~[wlfullclient.jar:12.2.2.0.0]
         at weblogic.net.http.SOAPHttpURLConnection.getInputStream(SOAPHttpURLConnection.java:41)
 ~[wlfullclient.jar:12.2.2.0.0]
         at ca.bell.webservice.WebServiceHandler.invokeWebService(WebServiceHandler.java:56)
 [MigrationTool.jar:?]
         at ca.bell.webservice.ExecuteMigrationWork.performRun(ExecuteMigrationWork.java:40)
 [MigrationTool.jar:?]
         at ca.bell.workmanagement.work.Work.call(Work.java:155) [MigrationTool.jar:?]
         at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_131]
         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 [?:1.8.0_131]
         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 [?:1.8.0_131]
         at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]

【问题讨论】:

    标签: java multithreading sockets http httpurlconnection


    【解决方案1】:

    有趣。让我们从最近的异常源开始分析您的堆栈跟踪。从AbstractPlainSocketImplsource code 开始,强制执行的第一个前提条件是:

    public void setOption(int opt, Object val) throws SocketException {
      if (isClosedOrPending()) {
        throw new SocketException("Socket Closed");
      }
    ...
    

    由于您的HttpURLConnection 的范围仅限于方法invokeWebService,我们可以排除套接字上的并发操作的假设,并且由于看起来不错的方法并且没有关闭套接字,我相信isClosedOrPending 返回 true 因为套接字的文件描述符是 null:

    public boolean isClosedOrPending() {
        /*
         * Lock on fdLock to ensure that we wait if a
         * close is in progress.
         */
        synchronized (fdLock) {
            if (closePending || (fd == null)) {
                return true;
            } else {
                return false;
            }
        }
    }
    

    这个分析中有问题的是为什么fd 文件描述符是null。然而,这在现阶段并不重要。如果我们相信我们的假设,我们可以得出的一个结论是,您正在为您的系统创建过多的连接来处理。

    我们如何解决这个问题?这里我们需要退后一步,看看是否可以重构代码来限制并发连接数。我需要有关您的具体实现的更多详细信息,以缩小原本无限的设计候选范围。例如,连接池是否合适?我们应该实施背压并限制客户端吗?我们应该水平扩展并分配负载吗?

    【讨论】:

    • 谢谢亚历克斯。我的要求是实现多线程 java 工具,该工具从 DB 中读取数据、构造 Web 服务请求、调用服务并根据 Web 服务响应以状态(成功/失败)更新 DB。现在,为了实现这一点,我使用了 ThreadPoolExecutor 框架。从属性文件中动态获取核心和最大线程大小,并为每个 Web 服务请求创建工作项,并为每个工作项调用 invokeWebservice()。
    • 感谢@user1510335 - 我很想知道有助于分析负载的各种指标 - 每秒(或分钟)的中位数/峰值请求是多少?你的线程池的大小是多少,你如何使用它?您是否在为客户的请求提供服务之前对其进行排队?
    • 嗨,Alex,每秒的峰值请求数约为 400,最大线程大小也设置为 400。如果达到最大线程数且没有明确的代码,Framework(ThreadPoolExecutor) 将负责对请求进行排队已编写用于管理线程。
    • @user1510335: 好的,所以打开连接的 400 个线程太多了 - 快速检查将尝试将最大线程数减少到 50 或 100,看看会发生什么 - 如果有太多为客户争吵?
    猜你喜欢
    • 2015-07-22
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-17
    • 2015-06-18
    • 2021-03-09
    • 1970-01-01
    相关资源
    最近更新 更多