大连仟亿科技
客服中心
  • 电话
  • 电话咨询:0411-39943997
  • 手机
  • 手机咨询:15840979770
    手机咨询:13889672791
网络营销 >更多
您现在的位置:仟亿科技 > 新闻中心 > 常见问题

网页消失的查询请求

作者:billionnet 发布于:2012/6/17 17:16:46 点击量:

 

在公司排查问题时遇到一个很奇怪的case。

网站前端业务逻辑用Jsp写的,后端数据库用C写的。JSP与数据库之间采取socket通信,连接为长连接。
有客户反映,打开网页后没有查到数据,经过排查JSP前端的日志里记录了用户的这次请求,而后台数据库的访问日志里却没有这个请求记录。

通过检查JSP业务端的日志发现,这种情况出现的很少,一个月中只有几天出现,一天也就出现几次,而且在JSP业务端的日志中都有记录,但是在后台数据库中没有记录,这条查询请求莫名奇妙的消失了。

JSP业务端的记录了,该请求到达的时间,查询条件(类似于sql语句),以及从后台数据库的返回结果,奇怪的是日志中显示从后台数据库的返回结果全部为0,但是后台数据库是正常情况下是不会返回全为0的结果的(除非有bug)。

那么究竟是JSP的程序写错了?还是后台数据库模块出现了问题呢?

JSP端的代码(伪java代码)

try {
  // code ...
  bool valid = isConnectionValid(socket);
  if (valid) {
	in = socket.getInputStream();
	out = socket.getOutputStream();
	out.write(request);
	byte[] data = new byte[HeadLength];
	in.read(data);
	Log(data);                       // log data
  }  
} catch(Exception e) {
  Log.warning(e);
}

首先会判断该连接是否可用,如果是可用的那么发送请求头,发送之后new一个接受体,然后等待后台数据库的相应,接收到数据后将接收到的内容记录到日志中。

看上去没什么问题,如果网络出现异常那么sokcet抛出Exception。

由于本人是写C的,由于C语言没有异常机制,所以必须通过检查返回值来判断函数是否成功执行。所以我查了JDK中的read。

public int read(byte[] b)
         throws IOException
 
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。
在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。 
 
如果 b 的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。
如果因为流位于文件末尾而没有可用的字节,则返回值 -1;
否则,至少读取一个字节并将其存储在 b 中。

发现read其实是可以返回0或者-1的,本case b的长度不为0,那么什么叫流位于文件末尾而没有可用的字节呢??

对于文件,读到文件的末尾没有可用字节。对于socket什么情况下会到达末尾呢?
其实这个比较简单,当服务器端写完数据后会close socket,这便是socket的结束标记。

  out = socket.getOutputStream();
  out.write(buffer);
  out.close();           // 流结束标记

也是说引发这个查询消失的原因是:客户端连接服务器后,服务器没有返回任何数据,就关闭了连接,而客户端的代码没有检查read的返回值,就进行了后面的操作。

由于客户端事先检查了连接是可用的才发送的请求,所以在read之后没有检查返回值。而java把new出来的data默认填0,因此客户端还以为服务器返回了0.

那么服务器(后台数据库)为什么会没有返回数据就关闭连接呢?

主要是长连接的缘故,对于不活动的长连接,当不活动的时间到达一定的阈值后,服务器机会关闭这些连接,这就存在了一个时间差,服务找到一个长时间不活动的长连接,准备关闭这个长连接,os在此时进行了一次进程调度,然后客户的请求来了,连接到了这个即将关闭的连接,并且发送了数据,之后服务器进程休眠结束,关闭了此链接。

而客户端发现sokcet到了末尾,于是返回-1,并且将data的内容打印出来,data初始化为0,因此log中有全为0的记录,但是服务器却没有这条记录。这就是请求的全过程。

客户端                                                     服务器            
							准备关闭socket
测试socket是否存活(ok)                                                             
连接  ok
 
发送数据 ok
                                                    长连接超时,关闭socket			  
 
没有检测read返回值,数组中
的数据默认为0

服务器在关闭sokcet之前不会再次检查这个socket是否又有新的连接,这种情形其实是很难出现的,因为要恰好再关闭sokcet之间进行一次调度,而且恰好一个请求过来了,这也解释了为什么这种现象会很少出现。

通过这个case,有几点值得我们注意:

  1. 即使是用java,我们也要检查函数的返回值。
  2. 在c中写一个已经关闭的socket会收到SIGPIPE信号,而读一个关闭的sokcet则会返回-1
  3. 通过实验发现,java读取一个关闭的socket会返回-1,而不会抛出异常
  4. Java会初始化new出来的数组,默认填0


分享到:


评论加载中...
内容:
评论者: 验证码:
  

Copyright@ 2011-2017 版权所有:大连仟亿科技有限公司 辽ICP备11013762-1号   google网站地图   百度网站地图   网站地图

公司地址:大连市沙河口区中山路692号辰熙星海国际2215 客服电话:0411-39943997 QQ:2088827823 42286563

法律声明:未经许可,任何模仿本站模板、转载本站内容等行为者,本站保留追究其法律责任的权利! 隐私权政策声明