知识库

限制 Bolt 线程与连接

在大量读/写事务请求的情况下,一些入口事务可能会被 Neo4j 服务器拒绝,并在 Neo4j debug.log 中记录以下错误

ERROR [o.n.b.r.MetricsReportingBoltConnection] Unable to schedule bolt session <session_id> for execution since there are no
available threads to serve it at the moment. You can retry at a later time or consider increasing max thread pool size for
bolt connector(s). Task java.util.concurrent.CompletableFuture$AsyncSupply@9bde0657 rejected from
org.neo4j.bolt.runtime.CachedThreadPoolExecutorFactory$ThreadPool@95fe540f[Running, pool size = 400, active threads = 400,
queued tasks = 0, completed tasks = 197942]

虽然上述建议的 bolt 线程池大小可以在 neo4j.conf 中配置,但人们可能会期望 bolt 连接的总数(等于活跃 + 空闲 bolt 连接,指标 neo4j.bolt.connections_running 与 neo4j.bolt.connections_idle 报告的数值)始终小于配置的 dbms.connector.bolt.thread_pool_max_size 的值。然而这是一种误解,本文旨在澄清此问题。

Bolt 线程与 Bolt 连接的区别

Bolt 线程是 Neo4j 服务器分配的 CPU 中用于执行特定任务的过程执行器。Bolt 连接是由客户端驱动会话 Neo4j 服务器 发起 的请求。在 JVM 服务器中,监听套接字上的接受线程接受连接并将其放入连接队列。线程池中的请求处理线程随后从队列中取出连接并服务这些请求。在每请求一个线程的模型中,线程仅在请求被处理期间与之关联,也就是说,同等数量的客户端连接只需更少的线程来处理。由于线程占用大量资源,这意味着服务的可扩展性更好。

The dbms.connector.bolt.thread_pool_max_size 指标报告了 bolt 线程 池的最大大小,限制线程池并不限制 连接 的总数量。一个线程可以处理众多连接。bolt 线程池的大小仅限制 Neo4j 服务器能够并发处理的 bolt 线程数量。如果连接处于非活动状态,则不会为该连接分配线程。目前服务器对空闲连接没有限制。不过,在客户端创建驱动时可以配置 连接 池大小限制。由于默认情况下连接没有上限,驱动的连接池大小可能会增长到机器 CPU 所能承载的全部可用连接数。

什么是空闲的 bolt 连接?

指标名称 neo4j.bolt.connections_idle 可能会让人认为空闲的 bolt 连接是指已完成 bolt 事务后仍保持打开但空闲状态的连接,直到线程池将其关闭。

以上是一种误解,因为在 neo4j.conf 中设置 dbms.connector.bolt.thread_pool_max_size 只限制了峰值并发线程所处理的连接数(由 neo4j.bolt.connections_running 指标表示)。然而空闲连接,即 被处理的连接,并不受此配置限制。线程池配置(服务器端)无法用于调节连接(客户端)。

为什么空闲连接指标命名为 neo4j.bolt.connections_idle?

因为这些连接是由驱动程序建立的,驱动程序在其生命周期内会复用连接。bolt 连接池是驱动的配置。线程池是服务器的配置,用来指定处理来自连接的任务的峰值容量。如果一个连接没有任务,它就在 客户端侧 处于空闲状态。服务器并不控制这些空闲连接,只有驱动可以关闭它们。此外,服务器没有连接数量的上限,空闲连接不应消耗 CPU。

如何控制客户端驱动打开的最大连接数?

驱动的连接限制可以通过 MaxConnectionPoolSize、MaxConnectionLifetime 和 ConnectionAcquisitionTimeout 参数进行配置。下面是 Neo4j Java 驱动的示例配置:

Config config = Config.builder()
            .withMaxConnectionLifetime( 30, TimeUnit.MINUTES )
            .withMaxConnectionPoolSize( 50 )
            .withConnectionAcquisitionTimeout( 2, TimeUnit.MINUTES )
            .build();
Driver driver = GraphDatabase.driver( uri, AuthTokens.basic( user, password ), config );

参考文献

© . This site is unofficial and not affiliated with Neo4j, Inc.