在上一篇中介绍了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(); } }
相关推荐
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....
eclipse jetty插件,从...下载run-jetty-run.zip文件,解压后再编写个links文件丢到eclipse的dropins目录下即可,省去了使用eclipse update方式安装的麻烦。 link文件样例如: path=d:\\eclipse_plugins\\run-jetty-run
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 ...
jetty是什么 jetty配置 jetty使用 jetty嵌入 jetty启动 jetty部署 jetty教程 jetty嵌入式 jetty
.jetty
jetty嵌入式服务器开发所必须的jar包,本人使用jetty版本为6.1.3,jar包总数为9个,来自jetty:commons-el-1.0.jar,jasper-compiler-5.5.15,jasper-compiler-jdt-5.5.15.jar,jasper-runtime-5.5.15.jar,jetty-...
jetty-9.4.6
jetty相关所有jar包,包含jar包: jetty-continuation-8.1.15.v20140411,jetty-http-8.1.15.v20140411,jetty-io-8.1.15.v20140411,jetty-security-8.1.15.v20140411,jetty-server-8.1.15.v20140411,jetty-util-8.1.15...
jetty 学习资料合集 jetty 学习资料合集 jetty 学习资料合集 jetty 学习资料合集
Jetty配置支持HTTPS以及受信网站证书生成方式
maven集成jetty必须jar包maven-jetty-plugin,内含多个版本
Jetty 7是Jetty奔向Eclipse后发布的第一个版本,本次的Jetty 7 RC2带给了我们一个十分诱人的新特性-支持跨域名Ajax请求。众所周知因为安全的原因,多数浏览器都限制了Ajax跨域请求和javascript加载的时候只能是与...
android i-jetty servlet-api-2.5.jar jetty-servlet-7.6.0.RC4.jar jetty-server-7.6.0.RC4.jar jetty-http-7.6.0.RC4.jar
jetty服务器,9.2版本适合java7+开发环境。 Jetty是一个纯粹的基于Java的网页服务器和Java Servlet容器。 尽管网页服务器通常用来为人们呈现文档,但是Jetty通常在较大的软件框架中用于计算机与计算机之间的通信。 ...
jetty源代码下载 jetty源代码下载 jetty源代码下载 jetty源代码下载
Jetty嵌入项目代码中示例,现我有一示例项目 e:/workspace/web-demo(称为project_home),里面的Web根目录是WebContent。 在project_home建一个jetty目录,子目录如:contexts、etc、lib。 把${jetty_home}/etc...
Jetty9 配置使用HTTPS证书,访问你的服务器更安全,更好的配置方法。
jetty 9.4.9, jetty 容器是轻便型容器,启动速度的确可以 ,附带有servlet-api.jar 和jsp-api.jar 两个jar包
jetty
自己写的jetty6在eclipse启动中配置说明