一【BUG描述】

最近开发一个Web系统的过程中遇到了一个诡异的BUG,花了2天时间解决,感觉如释重负。

这个BUG的现象是这样的:一个很普通的JSP网页,本来显示很正常,后来我在这个html页面上加了一些控件元素,并修改了下CSS, JavaScript代码,并加入了些中文字符。本来是觉得没什么问题,可是在服务器上跑起来就发现浏览器上这个页面显示一片空白,同时其他页面却显示正常。另外,我用的Tomcat版本是8.0.17。

那么我第一反应就是这个页面代码哪里改错了,可是我仔细看了下觉得又没什么问题,然后页面字符编码和Content-type都是显示设置UTF-8编码。看来前端好像没什么问题,然后我就在浏览器上查看页面源码,结果发现浏览器上显示的HTML源码居然不完整,html页面尾部的4~5行代码全部不见了。这就是导致页面无法正常显示的直接原因。

这个无非就两种可能:浏览器数据没有全部接收,或者服务器数据没有全部发送。这时,我就用WireShark抓包观察,结果显示tomcat服务器在发送数据时最后几行数据根本就没有发送过来。而且这儿非常巧合,因为html数据以chunked的方式发送,最后那几行数据恰好应该全部在最后的一个chunked包里面,换句话说,就是最后一个chunked包丢了。

为啥偏偏这个页面的最后一个chunked包丢了,而其他页面好好的呢?

这时没别的办法啦,只能一步一步调试JSP页面代码咯(这个耗费了我1天多的时间)。通过观察其他正常显示的JSP页面与这个出问题的JSP页面的执行流程,我发现问题出在:JSP页面在tomcat中输出的时候,最后一个chunked数据包在缓存中没有被flush。

二【定位】

为啥不flush?这个多种原因造成的:

  1. JSP页面在通过JspWriter输出之后,其实还留了点在缓存里面。以前一般是在JSP执行完,销毁PageContext的时候将缓存中的数据flush到网络流上,可现在作者为了提高网络性能,只是将数据flush到了较低一层的CoyoteWriter的缓存中,等到整个请求-响应过程结束,再flush到网络流上。如下图:

               
  2. 作者想法是不错,可惜少算了一步。CoyoteWriter对象是通过一个org.apache.catalina.connector.OutputBuffer来完成数据输出的。这个OutputBuffer对象有个suspended(暂停)标志位,用来随时暂停这个输出对象,处于暂停状态的OutputBuffer对象是不会进行数据输出的。tomcat在完成JspWriter输出的工作后,提前将OutputBuffer的状态设置为suspended状态,这一步的意思防止响应数据输出之后又输出其他不该输出的数据。这个本意是好的,可是等到整个过程结束,关闭OutputBuffer对象的时候,就因为这个suspended状态导致close这个函数还没来得及执行flush就直接退出了。如下图:
               
为啥别的页面没有这个情况,偏偏这个Html页面就出现问题了?这个情况有点复杂:
  1. 当页面通过OutputBuffer输出第1个chunked包的数据的时候,这个对象偷偷将这个chunked包的字符数据转换成二进制数据缓存了起来,并没有真正输出到网络流中。只有等到要输出第二个chunked包的时候,才flush缓存,将第一个包的数据输出到网络中,为第二个chunked包腾出足够的缓存空间,并缓存第二个chunked包。
  2. 当OutputBuffer输出第一个chunked数据包到网络中的时候,会抢先输出的Http 响应头,并将response对象的commited标志设置为true(这就表示响应头已经发送)。
  3. 在ErrorReportValve.invoke函数中,处理完http请求后,会判断response对象的commited标志是否为true,如果不是true,则将OutputBuffer的suspended标志设置为false,保证响应数据能够输出。
  4. 好了,原因找到了:因为其他web网页比较小,导致jsp页面处理结束了,所有的chunked数据还在缓冲中,完全并没有被输出到网络中(一部分在OutputBuffer缓存中,一部分在JspWriter缓存中),commited标志此时为false,等到ErrorReportValve处理时,根据commited标志,将suspended标志重置为false,这样在最后关闭OutputBuffer的时候,就将所有这些缓存中的数据一个个都flush到了网络上。相反,我修改的那个网页比较大,导致OutputBuffer中途就将部分数据flush到了网络上,是的commited标志位true,那么ErrorReportValve也就不会将suspended标志重置为false,导致OutputBuffer在关闭时,并不会flush残留在缓存中的数据。

三【解决】

如何解决这个bug?
  1. 在JSP页面最后加<% out.flush %>强制刷新;
  2. 升级或者回退Tomcat 服务器版本;
后面的Tomcat(8.0.23)版本是怎么解决这个bug的?
在StandardHostValve.invoke函数中加了一行代码,重置OutputBuffer的suspended标志为false,这样就可以保证关闭OutputBuffer时,flush残留在缓存中的数据了。如下图:

解决了一个Web网页显示不全的BUG相关推荐

  1. IE11更新导致部分网页显示不全、部分软件显示白屏、黑屏的解决办法

    IE更新惹的祸 IE11更新后,出现了一些.首先,发现部分网页显示不全,一些模块一直处于加载状态不能正常显示:然后TX 官方助 手 的页面也出现了白屏的样子,不能正常显示:坦克世界的登陆界面一直显示& ...

  2. 电脑ping服务器ip显示数据丢失,Win7系统如何测试网络丢包率解决网页显示不全的问题...

    Win7系统在上网过程中打开网页经常遇到网页显示不全,或者玩游戏卡顿的现象,但是过一会儿又恢复了.怎么回事呢?可能是因为网络丢包率太高导致的,我们可以Ping一下网络,找到故障原因.那么接下来小编和大 ...

  3. iPhone网页显示不全(被遮挡)怎么办?

    iPhone网页显示不全(被遮挡)怎么办? 今天再次遇到了这样一个历史遗留问题:我们在用iPhone浏览部分网页时,有时候会出现部分选项被界面遮挡的情况,如: 如何解决 今天无意间调整了一下网页字体: ...

  4. Win7旗舰版系统网页显示不全怎么办

    大家在使用IE浏览器浏览网页时,偶尔会出现网页显示不正常或者页面显示不全的问题,不知道是网络的问题还是什么.那么Win7旗舰版系统网页显示不全怎么办?就此问题,小编就给大家整理一下关于win7旗舰版网 ...

  5. python 横坐标只显示部分数据_解决echarts中横坐标值显示不全(自动隐藏)问题

    echarts中,横轴数据如果非常多,会自动隐藏一部分数据,我们可以通过属性interval来进行调整. 如下图,当横轴时间为13天时,echarts会自动隔天显示 如果我们想显示全,则需要在xAxi ...

  6. 本地html文件显示不全,网页显示不全,详细教您网页显示不全怎么办

    随着互联网的应用的发展,人们对网络世界的依赖越来越大,现实生活与网络世界密不可分.是否是浏览网页的时候出现过网页显示不正常,还有页面显示不全的问题,下面,小编跟大家讲解网页显示不全的解决步骤. 互联网 ...

  7. 解决 select2 开启 tags 输入中文显示不全的BUG

    解决 select2 开启 tags 输入中文显示不全的BUG 一.急着修复,不求甚解: 二.分享Debug思路 三.如果你不想动源码,还有一个非完美修复: 相关BUG 一.急着修复,不求甚解: 直接 ...

  8. 信创办公--基于WPS的Word最佳实践系列(解决WPS插入图片后显示不全问题)

    信创办公–基于WPS的Word最佳实践系列(解决WPS插入图片后显示不全问题) 项目背景 本篇文档是解决WPS插入"嵌入式"图片显示不全的问题.一般造成这个问题的情况是因为图片插入 ...

  9. 你写一个web网页小游戏

    写一个 web 网页小游戏需要以下几个步骤: 选择一种编程语言,常用的有 HTML.CSS.JavaScript 和 Python. 使用编辑器创建一个 HTML 文件,这个文件将是你的网页的基础. ...

最新文章

  1. 机器学习模型质量评价标准 — 精准率、召回率
  2. 【log】12/11 checking project:(Laravel)snsTest
  3. LOJ#2127「HAOI2015」按位或
  4. 设计模式--观察者(Observer)模式
  5. 程序员过关斩将--互联网人必备知识cookie和session认证
  6. Question of the Day: Microsoft | Database, Multiple Questions in One
  7. firefox 和 ie 事件处理的细节,研究,再研究-----书写同时兼容ie和ff的事件处理代码...
  8. 基础编程题之牛客网星际密码
  9. Android圆角图片封装类--copy别人的,不能转载,我也就醉了,谢谢原创
  10. 资源位图android4.2中为什么要高效的处理位图资源
  11. kettle转换和作业插件开发及调试
  12. linux 共享内存_linux进程间通信----IPC篇(一)----共享内存初识篇
  13. BitmapFactory.decodeResource(res, id); 第一个参数跟第二个参数有什么关系?
  14. 数组的数据查找c语言,【查找数组面试题】面试问题:c语言实现数据… - 看准网...
  15. Matlab 检测直线并求解直线方程
  16. Spring注解扫描原理浅析
  17. 微信模板消息发送帮助类
  18. linux系统支持网银吗,Linux系统能使用网银吗?Linux网银使用方法介绍
  19. vuetify学习第6天之v-btn-toggle---按钮组
  20. 2012-2022:深度学习十年后是撞墙了吗?Hinton、LeCun、李飞等大佬纷纷发声

热门文章

  1. 计算机考试用户名和密码,全国计算机等级考试报名系统账号注册和登录
  2. 双重预防体系智能管理系统助力集团施工企业落实双重预防机制建设
  3. win7硬件要求_九代酷睿平台装Win7的方法!2019年九代i5-9400F配B365装机配置单
  4. 我的新能源动力电池PACK热管理仿真案例
  5. 微信小程序中使用vant框架
  6. NeurIPS 2019 | 香侬科技开源Glyce2.0,中文字形增强BERT表征能力
  7. 最新可用智云影视资源网PHP采集无需数据库V1.1版
  8. STM8S005 TIM1定时器PWM应用
  9. spring框架学习(一):Bean的装配方式 ——基于注解的装配、自动装配
  10. 语音处理的算法和方法研究(Matlab代码实现)