`

jetty链接超时过期

阅读更多

Jetty误判长连接为超时连接的问题

在上一篇中介绍了jetty的反映器模型,selector线程与业务子线程交互的点有:

1、分发事件给子线程做,启动子线程;

2、子线程发现阻塞或者连接关闭等时间时,注册内部changes,等待selector线程调度;

3、检测超时连接,并且关闭连接。

 

在检测超时连接上面,jetty存在较多的问题,可能会误判。下面是一个典型的问题,问题一步一步定位的过程也是非常艰难和曲折的,但是最终问题找到的时候却发现不过如此。 
具体的问题出在下面这个判断表达式上面,下面这个方法是由主线程派生一个子线程的来调用的,在这个线程里面对所有的连接进行超时检查。

// Idle tick
if (now-_idleTick>__IDLE_TICK)
{
    _idleTick=now;
    
    final long idle_now=((_lowResourcesConnections>0 && selector.keys().size()>_lowResourcesConnections))
        ?(now+_maxIdleTime-_lowResourcesMaxIdleTime)
        :now;
        
    dispatch(new Runnable()
    {
        public void run()
        {
            for (SelectChannelEndPoint endp:_endPoints.keySet())
            {
                endp.checkIdleTimestamp(idle_now);
            }
        }
    });
}

 

被调用的方法:

/* ------------------------------------------------------------ */
public void checkIdleTimestamp(long now)
{
    if (_idleTimestamp!=0 && _maxIdleTime!=0 && now>(_idleTimestamp+_maxIdleTime))
    {
        idleExpired();
    }
} 

 

 

_idleTimestamp是每个连接实例中标志连接上次空闲的开始时间,如果该值为0,则说明连接处于非空闲,当子线程在处理请求时会频繁地将该变量在0值与空闲的时间点之间切换,而该变量本身是寄存器变量,可以保证变量本身在多线程之间的同步。但是上面是一个表达式,假如运行的时序是下面这样的:

1、子线程刚刚将连接置为空闲,那么_idleTimestamp大于0;

2、主线程开始判断该连接是否超时,判断到_idleTimestamp>0满足;

3、当程序继续运行时,子线程又将_idleTimestamp切换为0;

4、主线程开始判断now>(_idleTimestamp+_maxIdleTime)的条件,发现也满足;

5、最终,所有判断条件均为真,进入关闭连接的流程,连接被关闭。

这样就形成一个误判。本来连接上的请求正在处理,却被提前关闭了,最终会导致某一个请求处理失败,并且是莫名其妙的失败,网络抓包能发现是服务端关闭了连接,但是到底是谁关闭了连接都很难知道,最终通过在关闭socket的地方记录堆栈,待到堆栈累计到一定程度时,将其打印到日志中,结合抓包数据就能发现问题所在。

问题明确了,修改方法也简单:

/* ------------------------------------------------------------ */
public void checkIdleTimestamp(long now)
{
    long idleTimestamp = _idleTimestamp;
    if (idleTimestamp!=0 && _maxIdleTime!=0 && now>(idleTimestamp+_maxIdleTime))
    {
        idleExpired();
    }
}

 

 

这样就避免了在计算表达试的过程中全局变量的值被改变了。问题解决。

这个问题解决之后,紧随其后,又发现了另外一个更加奇怪的现象:

发现在连接启动之后,业务已经在处理中,最终业务处理完成,但是对外回复响应时却发现连接已经被关闭了。这个问题与上面的问题很像,但是不是同一个问题。经过上面的分析定位,对这里已经有一定的积累,很快就找到了连接关闭的根源,还是在检查超时连接上面。

经过反复推敲,发现问题出在下面3个函数的配合上面,当一个连接上面接近200秒(连接最大超时时间)都没有任何请求,而此时一个新的请求已经启动,但是还没有来得及调用cancelIdle来置空闲标志为0,这时请求已经开始处理,就在这个时候,主线程开始检查连接是否超时,结果发现已经超过200秒,连接上面没有处理任何数据,走入关闭连接的流程。

而此时业务线程已经在处理数据,等待数据处理完成,需要回复响应的时候才发现此连接已经断开了。这样客户端无法收到任何讯息。服务端记录该请求失败,但实际上请求已经处理成功。

/* ------------------------------------------------------------ */
public void scheduleIdle()
{
    _idleTimestamp=System.currentTimeMillis();
}

/* ------------------------------------------------------------ */
public void cancelIdle()
{
    _idleTimestamp=0;
}

/* ------------------------------------------------------------ */
public void checkIdleTimestamp(long now)
{
    long idleTimestamp = _idleTimestamp;
    if (idleTimestamp!=0 && _maxIdleTime!=0 && now>(idleTimestamp+_maxIdleTime))
    {
        idleExpired();
    }
}

这个问题的修改方法稍微复杂一点:

 
/* ------------------------------------------------------------ */
public void scheduleIdle() throws IOException
{
    synchronized (idleClosing)
    {
        if (idleClosing.get())
        {
            throw new IOException("the connection was closed right now. this request will be responsed a wrong value.");
        }
        else
        {
            _idleTimestamp=System.currentTimeMillis();
        }
    }
}

/* ------------------------------------------------------------ */
public void cancelIdle()
{
    _idleTimestamp=0;
}

/* ------------------------------------------------------------ */
public void checkIdleTimestamp(long now)
{
    long idleTimestamp = _idleTimestamp;
    if (idleTimestamp!=0 && _maxIdleTime!=0 && now>(idleTimestamp+_maxIdleTime))
    {
        synchronized (idleClosing )
        {
            if (_idleTimestamp == 0)
            {
                Log.warn("the request is doing. it cannot be closed!");
                return;
            }
            else
            {
                idleClosing.set(true);
            }
        }
        idleExpired();
    }
}

 

 
在两个方法之间用一个标志量来作为同步依据,当关连接的事情先发生,那么切换状态的操作就会因为idleclosing标志已经被置而直接抛出异常,不至于子线程继续处理数据。如果是子线程置连接为非空闲在前,则主线程的关连接操作也会不关连接而直接返回。
 
 
 
分享到:
评论

相关推荐

    jetty所需jar包

    最后,了解如何配置Jetty的启动参数和设置也是非常重要的,例如端口号、日志级别、会话超时时间等。这通常通过创建一个jetty.xml配置文件来完成,或者通过代码动态配置。 总结,启动Jetty所需jar包包括了Jetty的...

    jetty相关的全部jar包

    jetty-security-9.4.8.v20171121.jar,jetty-io-9.4.8.v20171121.jar,jetty-continuation-9.4.8.v20171121.jar,jetty-client-9.4.8.v20171121.jar,jetty-jmx-9.4.8.v20171121.jar,jetty-plus-9.4.8.v20171121....

    jetty各个版本下载

    Jetty是一款轻量级、高性能的Java Web服务器和Servlet容器,它被广泛应用于各种规模的项目,从小型的个人项目到大型的企业级应用。Jetty以其高效、稳定和易于集成的特点,深受开发者喜爱。在本篇文章中,我们将深入...

    jetty-6.1.26.zip

    这些连接器和适配器增强了Jetty对不同网络环境的适应性。 9. **安全性**:Jetty提供了安全模块,支持基本的认证机制,如HTTP Basic和Digest,以及SSL/TLS加密,确保数据传输的安全性。 10. **Maven插件**:对于...

    jetty-all.jar

    它包括了异步I/O、NIO和线程池等相关实现,使得Jetty能够高效地处理大量的并发连接。 最后,jetty-webapp-9.4.14.v20181114.jar提供了对Web应用程序的支持。它包含了解析和加载WAR文件、管理Web应用上下文...

    jetty 8及依赖包

    NIO允许单个线程服务多个连接,通过选择器(Selector)监控多个通道(Channel),当数据到达时才进行读写操作,从而提高了服务器的吞吐量和响应速度。 在Jetty 8中,Servlet容器是其核心功能之一,它负责管理和调度...

    jetty 适合jdk1.8用的服务器

    1. **性能优化**:Jetty以其高效的性能而著称,它使用非阻塞I/O模型,如NIO和EPOLL,这使得它在处理大量并发连接时表现出色。 2. **轻量级**:Jetty没有像Tomcat那样的复杂配置,它的设计目标是简洁,因此启动快速,...

    实战 Jetty--让你快速速学会jetty

    在可扩展性方面,Jetty针对高并发和长时间连接的Web 2.0应用进行了优化。它利用Continuation机制有效地处理大量用户请求和长连接,避免了线程和内存资源的过度消耗,确保系统在面临大量服务请求时仍能保持良好的性能...

    自己构建微服务(springmvc+内嵌jetty+maven 环境配置)

    在博文链接中,可能会详细解析上述步骤中的源代码,包括如何在Java类中配置Jetty,如何定义Spring MVC的Controller,以及如何在pom.xml中管理依赖。 8. **工具的使用**: 除了Maven、Jetty和Spring MVC,可能还会...

    jetty-6.1.14.zip

    Jetty提供了一套完整的会话管理机制,包括会话持久化、跨域会话共享和会话超时检测等。 9. **Web应用部署** 在Jetty中,可以通过部署WAR文件或配置XML文件来启动Web应用。此外,它还支持热部署,即在不中断服务的...

    jetty6.1.6-1

    4. **异步I/O**:Jetty利用Java NIO(非阻塞I/O)来提高性能,允许服务器同时处理大量连接,从而提高了并发性。 5. **模块化设计**:Jetty的模块化架构允许用户根据需求选择安装组件,降低了不必要的资源消耗。 6....

    jetty 6 指南书

    - **构架概述**:解释Jetty的核心组件,如Connector(连接器)、Handler(处理器)和Server。 - **Connector**:负责网络通信,如HTTP和HTTPS连接的建立。 - **Handler**:处理不同的HTTP请求,如Request Handler...

    gradle的jetty插件使用例子

    例如,可以创建一个名为`jetty.xml`的文件,用于设置服务器的线程池大小、连接超时等参数。这些配置将在运行`jettyRunWar`任务时被加载。 `src`目录是项目的源代码存放处,通常分为`src/main/java`和`src/main/...

    jetty 9.4.9

    Jetty 9.4.9 是一个开源的Java Web服务器和Servlet容器,以其轻量级、高效和灵活性而受到开发者的欢迎。这个版本是Jetty项目的一个重要里程碑,提供了许多性能改进和新特性。在深入探讨之前,让我们先了解一些基本...

    jetty-6.1.9.zip

    7. **性能与优化**: 在实际应用中,考虑到性能和负载,可能需要对Jetty和CometD的配置进行调整,例如设置连接超时、优化线程池大小、缓存策略等,以确保系统的稳定性和高效运行。 综上所述,"jetty-6.1.9.zip"提供...

    jetty在eclipse中配置

    Jetty是一款轻量级、高性能的Java Web服务器和Servlet容器,它被广泛用于开发、测试和部署Web应用程序。Eclipse是流行的Java集成开发环境(IDE),开发者可以利用它来管理和运行项目。本教程将详细介绍如何在Eclipse...

    解决jetty8内存溢出版本

    2. **优化Web应用**:检查应用代码是否存在内存泄漏,如未正确关闭数据库连接、线程池或文件流。同时,确保使用了有效的缓存策略,避免无限制地存储大量数据在内存中。 3. **合理配置线程池**:Jetty使用线程池处理...

    jetty.jar,jetty-sslengine.jar,jetty-util.jar

    jetty-sslengine.jar包含了处理SSL/TLS连接的类和方法,使Jetty能够处理基于SSL的请求,支持数字证书,实现服务器与客户端之间的安全通信。这使得开发人员可以轻松地在Jetty上搭建安全的Web服务,满足诸如电子商务...

    jetty6.1.6-2

    10. **连接器(Connectors)**:Jetty的连接器(如jetty-client.jar)负责网络通信,可以支持HTTP、HTTPS等多种协议。 总的来说,Jetty 6.1.6的lib目录包含了运行和管理Web服务器所需的所有核心组件和依赖,使得...

    Jetty多版本软件包

    Jetty软件包内容: jetty-distribution-9.4.51.v20230217.tar.gz jetty-distribution-9.4.51.v20230217.zip jetty-home-10.0.15.tar.gz jetty-home-10.0.15.zip jetty-home-11.0.15.tar.gz jetty-home-11.0.15.zip ...

Global site tag (gtag.js) - Google Analytics