• 2013-11-21

    战斗HTTP(上)

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/239188391.html

    去参加QCon的路上,一个同事给我打了个电话,他在使用Moco的过程中,遇到了一个奇怪的问题:测试会时不时的挂住。在不明就里的情况下,我给出了一些常规性的建议,换个新版本试试,要不,能不能把情况简化到最简单的情形,发给我试试。

    几天后,我从QCon回来,那个同事愁眉苦脸地找到我:换了新版本,问题依然,更奇怪的是,当试图用最简单的情形重现时,现象居然就消失了。用抓包工具试试吧,看看底层发送的消息是不是对的。我又想到了一个常规的解决方案。他同意了,尝试了,又失败了,它在中间加了个代理,结果现象又消失了。两边发送的消息从HTTP报文的内容上,看不出什么端倪。

    我这个同事在办公室里是出了名的执着,几天之后,他又跑了过来,我已经写了个测试,已经能够稳定的重现这个现象了。我们一起坐下来,用单步跟踪的方式追着代码。经过无数次的运行,我们惊奇地发现,挂住的地方根本没有走到Moco里面,当第一次访问Moco服务器成功之后,离开Moco代码,在Netty框架里,试图select下一个要处理的请求时,就挂住了。而且很有趣的现象是,如果两次访问Moco服务器时间间隔较长,比如5秒,万事大吉,如果时间很短,基本上就会挂住。我们甚至开始怀疑是不是Netty本身出了问题。

    第二天,这个同事又取得了新的进展,他已经把这个测试简化到完全不依赖于他们的工程。这样,我们就可以在别的机器上进行调试,而且简化到最简单的情形之后,我们只关注最重要的东西。这次现象是相同的,挂住,但跟进去的结果却截然不同,这回出问题的地方是客户端,当发送了第一个请求之后,试图进行第二次发送时,请求就挂住了。

    我能怎么办呢?我又仔细品味了阅读了一下Moco的代码,突然注意到里面有这样一句:

      future.addListener(ChannelFutureListener.CLOSE);

    这句话的意思是,当请求处理完成之后,关闭链接。似乎灵光闪现,我查看了一下客户端发起的请求,在HTTP头里面有这样一句:

      Connection: keep-alive

    这显然是HTTP协议里面,用来请求服务器端保持链接的一种方式。如果你不知道的话,在一些HTTP协议的实现中,有时为了节省链接建立的资源,会复用之前已经建立好的链接,当然,前提条件是,服务器和客户端都支持。

    显然,Moco是不支持的。但客户端又有这种期望,所以,挂住了。简单地删除了负责关闭的代码,代码顺畅地通过了测试。这里面有两个问题,Moco服务器端支持是一方面,客户端实现是另一方面,换句话,当服务器端不支持长链接时,客户端应该做相应的处理,至少这个客户端没有做到,顺便说一下,这个库是CXF。

    这不是故事的结尾。

    分享到:

    历史上的今天:

    买书不读 2005-11-21
    引用地址: