从远程服务器提交neo4j批处理时出现IncompleteRead错误;格式错误的HTTP响应

时间:2014-03-20 10:42:50

标签: http neo4j tcpdump py2neo

我在服务器A上设置了neo4j,我在服务器B上运行了一个连接到它的应用程序。

如果我在服务器A上克隆应用程序并运行单元测试,它可以正常工作。但是在服务器B上运行它们,安装程序运行30秒并因IncompleteRead而失败:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.1-py2.7.egg/nose/suite.py", line 208, in run
    self.setUp()
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.1-py2.7.egg/nose/suite.py", line 291, in setUp
    self.setupContext(ancestor)
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.1-py2.7.egg/nose/suite.py", line 314, in setupContext
    try_run(context, names)
  File "/usr/local/lib/python2.7/site-packages/nose-1.3.1-py2.7.egg/nose/util.py", line 469, in try_run
    return func()
  File "/comps/comps/webapp/tests/__init__.py", line 19, in setup
    create_graph.import_films(films)
  File "/comps/comps/create_graph.py", line 49, in import_films
    batch.submit()
  File "/usr/local/lib/python2.7/site-packages/py2neo-1.6.3-py2.7-linux-x86_64.egg/py2neo/neo4j.py", line 2643, in submit
    return [BatchResponse(rs).hydrated for rs in responses.json]
  File "/usr/local/lib/python2.7/site-packages/py2neo-1.6.3-py2.7-linux-x86_64.egg/py2neo/packages/httpstream/http.py", line 563, in json
    return json.loads(self.read().decode(self.encoding))
  File "/usr/local/lib/python2.7/site-packages/py2neo-1.6.3-py2.7-linux-x86_64.egg/py2neo/packages/httpstream/http.py", line 634, in read
    data = self._response.read()
  File "/usr/local/lib/python2.7/httplib.py", line 532, in read
    return self._read_chunked(amt)
  File "/usr/local/lib/python2.7/httplib.py", line 575, in _read_chunked
    raise IncompleteRead(''.join(value))
IncompleteRead: IncompleteRead(131072 bytes read)
-------------------- >> begin captured logging << --------------------
py2neo.neo4j.batch: INFO: Executing batch with 2 requests
py2neo.neo4j.batch: INFO: Executing batch with 1800 requests
--------------------- >> end captured logging << ---------------------

当我提交足够大的批次时会发生异常。如果我减小数据集的大小,它就会消失。它似乎与请求大小而不是请求数量有关(如果我向创建的节点添加属性,我可以减少请求)。

如果我使用batch.run()代替.submit(),我就不会收到错误,但测试失败了;好像批次被静默拒绝了。如果我使用.stream()并且不对结果进行迭代,则同样的事情发生在.run();如果我迭代它们,我会得到与.submit()相同的错误(除了它&#34; 0字节读取&#34;)。

查看httplib.py表明,当HTTP响应具有Transfer-Encoding: Chunked并且不包含需要一个块大小的块大小时,我们会收到此错误。所以我在测试中运行了tcpdump,事实上,这似乎正是发生的事情。最后一个块的长度为0x8000,其最终字节为

"http://10.210.\r\n
0\r\n
\r\n

(为了清晰起见,在\ n之后添加了换行符。)这看起来像正确的分块,但是第0x8000个字节是第一个&#34; /&#34;而不是第二个&#34;。&#34;。早八个字节。它也不是一个完整的响应,是无效的JSON。

有趣的是,在这个块中我们得到以下数据:

"all_relatio\r\n
1280\r\n
nships":

也就是说,它看起来像一个新块的开始,但嵌入在旧块中。如果我们注意到它开始,这个新块将在正确的位置(第二个&#34;。&#34;以上)完成。如果块标头不在那里,那么旧​​块将在正确的位置完成(后面的8个字节)。

然后我提取了批处理的POST请求,并使用cat batch-request.txt | nc $SERVER_A 7474运行它。对此的响应是一个有效的分块HTTP响应,包含一个完整的有效JSON对象。

我想也许netcat发送请求的速度比py2neo快,所以我介绍了一些减速

cat batch-request.txt | perl -ne 'BEGIN { $| = 1 } for (split //) { select(undef, undef, undef, 0.1) unless int(rand(50)); print }' | nc $SERVER_A 7474

但它继续发挥作用,尽管现在慢得多。

我也尝试在服务器A上执行tcpdump,但是对localhost的请求不要通过tcp。

我仍然有一些我无法探索的途径:我还没有弄清楚请求失败的可靠性或者在哪些条件下(我曾经看到它成功通过一个通常失败的批次,但是我还没有探索过这个界限。我没有尝试直接从python发出请求,而没有通过py2neo。但我并不特别期望这些中的任何一个都能提供非常丰富的信息。我没有密切关注TCP转储,除了使用wireshark&#39;关注TCP流&#39;提取HTTP会话;我真的不知道我在那里寻找什么。在失败的转储中,有一大部分wirehark以黑色突出显示,并且在成功的转储中只有孤立的线条变黑,这可能与此相关吗?

所以现在:有谁知道可能会发生什么?还有什么我应该尝试诊断问题吗?

TCP转储位于:failedsuccessful

编辑:我开始了解失败的TCP转储。整个对话大约需要30秒,而且两个服务器都在发送ZeroWindow TCP帧,差距为28秒 - 这些是我提到的黑线。

首先,py2neo填满了neo4j的窗口; neo4j发送一个框架说&#34;我的窗口已满#34;,然后另一个框架填满了py2neo的窗口。然后我们花了大约28秒钟,他们每个人只是说&#34;是的,我的窗户仍然是满的&#34;。最终neo4j再次打开它的窗口,py2neo再发送一些数据,然后py2neo打开它的窗口。他们两个都发送了更多的数据,然后py2neo完成发送请求,neo4j在完成之前发送更多数据。

所以我认为可能问题就是这样,他们都拒绝处理更多数据,直到他们发送了更多数据,并且在其他处理过程之前都不会发送更多数据。最终,neo4j进入了一个出错的地方&#34;循环,py2neo解释为&#34;继续发送更多数据&#34;。

这很有意思,但我不确定这是什么意思,从neo4j发送到py2neo的倒数第二个TCP帧开始\r\n1280\r\n - 伪块的开头。启动实际块的\r\n8000\r\n只是出现在一个不起眼的TCP帧的中途。 (这是py2neo完成发送帖子请求后发送的第三帧。)

编辑2:我检查了蟒蛇挂在哪里。不出所料,这是在发送请求时 - 所以BatchRequestList._execute()在neo4j放弃之前不会返回,这就是.run().stream()没有比{{1}更好的原因。 }。

1 个答案:

答案 0 :(得分:2)

似乎解决方法是设置标头X-Stream: true;format=pretty。 (默认情况下,它只是true; 使用非常漂亮,但由于this bug而被删除(看起来它实际上是neo4j bug,似乎仍然是开放的,但目前对我来说不是一个问题。)

看起来,通过设置format=pretty,我们会导致neo4j在处理完整个输入之前不发送任何数据。因此,它不会尝试发送数据,在发送时不会阻止,并且在发送内容之前不会拒绝阅读。

完全删除X-Stream标头,或将其设置为false,似乎与设置format=pretty具有相同的效果(如同,使neo4j发送一个分块的响应,非常漂亮-printed,不包含状态代码,并且在整个请求被处理之前不会被发送),这有点奇怪。

您可以使用

设置单个批次的标题
batch._batch._headers['X-Stream'] = 'true;format=pretty'

或者用

设置全局标题
neo4j._add_header('X-Stream', 'true;format=pretty')