【问题标题】:py2neo "ProtocolError: Server closed connection" when using Boltpy2neo 使用 Bolt 时出现“协议错误:服务器关闭连接”
【发布时间】:2017-01-12 20:06:49
【问题描述】:

最近一个使用py2neo 包并通过Bolt 协议连接的脚本开始意外失败并出现ProtocolError: Server closed connection,我很难理解为什么。请注意,当使用 HTTP 作为协议时,该脚本有效。

脚本提取 Neo4j 图形,在 Python 中对其进行扩充,然后尝试将相关更改推送回数据库。整个过程大约需要一个小时,并且可靠地工作了相当长的一段时间,但是最近推送阶段开始失败。

如果我创建一个玩具示例,绕过拉取和扩充,然后推送工作,因此我认为服务器由于超时而关闭了连接,但是我在 py2neo 中找不到任何与 Bolt 相关的超时参数.注意我已将 HTTP 套接字超时设置为 9999 秒(~ 2.75 小时),

from py2neo.packages.httpstream import http
http.socket_timeout = 9999

虽然我对此的理解是 i)它与 Bolt 无关,并且 ii)超时处于连接级别,大大超过了脚本运行的时间。

作为参考,我使用的是 Neo4j v3.0.3 和 py2neo v3.1.2。堆栈跟踪是:

File "/usr/local/lib/python2.7/dist-packages/py2neo/database/__init__.py", line 1017, in __exit__
self.commit()
File "/usr/local/lib/python2.7/dist-packages/py2neo/database/__init__.py", line 1059, in commit
self._post(commit=True)
File "/usr/local/lib/python2.7/dist-packages/py2neo/database/__init__.py", line 1291, in _post
self.finish()
File "/usr/local/lib/python2.7/dist-packages/py2neo/database/__init__.py", line 1296, in finish
self._sync()
File "/usr/local/lib/python2.7/dist-packages/py2neo/database/__init__.py", line 1286, in _sync
connection.fetch()
File "/usr/local/lib/python2.7/dist-packages/py2neo/packages/neo4j/v1/bolt.py", line 323, in fetch
raw.writelines(self.channel.chunk_reader())
File "/usr/local/lib/python2.7/dist-packages/py2neo/packages/neo4j/v1/bolt.py", line 174, in chunk_reader
chunk_header = self._recv(2)
File "/usr/local/lib/python2.7/dist-packages/py2neo/packages/neo4j/v1/bolt.py", line 157, in _recv
raise ProtocolError("Server closed connection")
ProtocolError: Server closed connection

精简后的 Python 代码是这样的,

import py2neo
from py2neo.packages.httpstream import http
http.socket_timeout = 3600

graph = py2neo.Graph(
    host='localhost',
    bolt=True,
    bolt_port=4096,
    http_port=4095,
)

# Pull the graph from the Neo4j database via graph.run(...) statements,
# augments the graph etc.
# ...

# Exception is thrown when the following push transaction is executed.
with graph.begin() as tx:
    statement = """
        UNWIND {rows} AS row
        WITH row.source AS source, row.target AS target
        MATCH (s:Entity)
        USING INDEX s:Entity(uuid)
        WHERE s.uuid = source
        MATCH (t:Entity)
        USING INDEX t:Entity(uuid)
        WHERE t.uuid = target
        MATCH (s)-[r:FAVORITED]->(t)
        DELETE r
    """

    rows = [{
        'source': '8267d7d0-a837-11e6-b841-22000bcec6a9',
        'target': 'c6296c97-a837-11e6-b841-22000bcec6a9',
    }]

    tx.run(statement, rows=rows)

对于如何进一步调试或导致连接关闭的原因,是否有人有任何建议?我查看了_recv 函数,但不清楚为什么套接字没有接收到数据。

查看 Neo4j debug.log 文件唯一可能的相关错误是

java.io.IOException: Connection reset by peer
at sun.nio.ch.FileDispatcherImpl.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at io.netty.buffer.PooledUnsafeDirectByteBuf.setBytes(PooledUnsafeDirectByteBuf.java:311)
at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:881)
at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:242)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:119)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
at java.lang.Thread.run(Thread.java:745)

我还检查了服务的正常运行时间,并确认它在脚本的整个持续时间内都在运行 (HH:MM:SS),

 > ps -p "14765" -o etime=
 16:55:31

【问题讨论】:

    标签: neo4j py2neo


    【解决方案1】:

    我认为问题在于:

    tx.run(statement, rows=rows)
    

    CypherResoure.run() 的第二个参数是一个参数字典。您将它们作为附加的 python 参数传递。见py2neo code

    试试:

    tx.run(statement, {"rows": rows});
    

    【讨论】:

    • 我们使用的是 py2neo 的 v3,它具有相同的 signatureparameters 参数是可选的,而 rows 被解释为“kwargs”。请注意,使用 HTTP 而不是 Bolt 时,代码可以正常工作。
    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 2019-09-29
    • 2022-01-22
    • 2019-05-24
    • 2012-10-04
    • 1970-01-01
    • 2017-04-05
    • 1970-01-01
    相关资源
    最近更新 更多