HttpServletResponse.flushBuffer()在超时连接时不会抛出IOException

时间:2013-01-23 17:01:40

标签: java servlets jboss

我面临着非常不寻常的情况。 我有两个通过HTTP进行通信的Jboss(7.1)实例。 实例A打开与实例B的HTTP连接,并发送一些要处理的数据。连接已设置超时,因此在N秒后如果没有读取响应,则抛出SocketTimeoutEception。执行一些清理并关闭连接。 实例B有一个servlet,监听这样的http请求,当收到一个请求时,就会完成一些计算。之后,填充响应并返回给客户端。

问题是如果计算花费太多时间,客户端(A)将因超时而关闭连接,但服务器(B)将正常进行并将在一段时间后尝试发送响应。我希望能够检测到连接已关闭并进行一些管理,但我似乎无法做到这一点。

我试过调用HttpServletResponse.flushBuffer(),但没有抛出任何异常。我还在http请求中明确设置了“Connection:close”以避免持久连接,但这没有任何效果。 http servlet共振被正常处理并在空白中消失而没有任何异常。我不知道我做错了什么,我在这个网站上读过其他问题,如:

Java's HttpServletResponse doesn't have isClientConnected method

Tomcat - Servlet response blocking - problems with flush

但它们在我的情况下不起作用。

我认为jboss servlet容器可能存在一些特定的东西,导致忽略或缓冲响应,或者尽管我努力从客户端关闭它,但可能会重用http连接(A)。如果你能提供一些指向问题的地方,我会很高兴。我花了好几天时间没有取得相关进展,所以我需要紧急解决这个问题。

以下是相关代码:

客户端代码(服务器A):

    private static int postContent(URL destination, String fileName, InputStream content)
    throws IOException, CRPostException
    {
        HttpURLConnection connection = null;

        //Create the connection object
        connection = (HttpURLConnection)destination.openConnection();

        // Prepare the HTTP headers
        connection.setDoOutput(true);
        connection.setInstanceFollowRedirects(false);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("content-type", "text/xml; charset=UTF-8");
        connection.setRequestProperty("Content-Encoding", "zip");
        connection.setRequestProperty("Connection", "close");//Try to make it non-persistent

        //Timouts
        connection.setConnectTimeout(20000);//20 sec timout
        connection.setReadTimeout(20000);//20 sec read timeout

        // Connect to the remote system
        connection.connect();

        try
        {
            //Write something to the output stream
            writeContent(connection, fileName, content);

            //Handle response from server
            return handleResponse(connection);
        }
        finally
        {
            try
            {
                try
                {
                    connection.getInputStream().close();//Try to explicitly close the connection
                }
                catch (Exception e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                connection.disconnect();//Close the connection??
            }
            catch (Exception e)
            {
                logger.warning("Failed to disconnect the HTTP connection");
            }
        }
    }

private static int handleResponse(HttpURLConnection connection)
    throws IOException, CRPostException
    {
        String responseMessage = connection.getResponseMessage();//Where it blocks until server returns the response 
        int statusCode = connection.getResponseCode();
        if (statusCode == HttpURLConnection.HTTP_OK)
        {
            logger.debug("HTTP status code OK");
            InputStream in = connection.getInputStream();

            try
            {
                if (in != null)
                {
                    //Read the result, parse it and return it
                    ....
                }
            }
            catch (JAXBException e)
            {
            }
        }// if

        //return error state
        return STATE_REJECTED;
    }//handleResponse()

服务器代码(服务器B):

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
{
    String crXML = null;
    MediaType mediaType = null;
    Object result;

    // Get the media type of the received CR XML
    try
    {
        mediaType = getMediaType(request);
        crXML = loadDatatoString(mediaType, request);
        result = apply(crXML);
    }
    catch (Exception e)
    {
        logger.error("Application of uploaded data has failed");

        //Return response that error has occured
        ....

        return;
    }

    // Finally prepare the OK response
    buildStatusResponse(response, result);

    // Try to detect that the connection is broken
    // and the resonse never got to the client
    // and do some housekeeping if so
    try
    {
        response.getOutputStream().flush();
        response.flushBuffer();
    }
    catch (Throwable thr)
    {
        // Exception is never thrown !!!
        // I expect to get an IO exception if the connection has timed out on the client
        // but this never happens
        thr.printStackTrace();
    }
}// doPost(..)

public static void buildStatusResponse(HttpServletResponse responseArg, Object result)
{
    responseArg.setHeader("Connection", "close");//Try to set non persistent connection on the response too - no effect

    responseArg.setStatus(HttpServletResponse.SC_OK);

    // write response object
    ByteArrayOutputStream respBinaryOut = null;
    try
    {
        respBinaryOut = new ByteArrayOutputStream();
        OutputStreamWriter respWriter = new OutputStreamWriter(respBinaryOut, "UTF-8");
        JAXBTools.marshalStatusResponse(result, respWriter);
    }
    catch (Exception e)
    {
        logger.error("Failed to write the response object", e);
        return;
    }

    try
    {
        responseArg.setContentType(ICRConstants.HTTP_CONTENTTYPE_XML_UTF8);
        responseArg.getOutputStream().write(respBinaryOut.toByteArray());
    }
    catch (IOException e)
    {
        logger.error("Failed to write response object in the HTTP response body!", e);
    }
}//buildStatusResponse()

1 个答案:

答案 0 :(得分:0)

您正在客户端遇到HTTP连接池。物理连接并未真正关闭,它将返回到池中以供以后重用。如果它在一段时间内处于空闲状态,它将被关闭并从池中删除。所以在服务器flushBuffer()发生的那一刻,连接仍然存在。

OR

刷新的数据足够小,可以放入发送方的套接字发送缓冲区,因此底层写入会立即成功返回,而断开连接只能在以后由TCP异步发现。