我有一项服务以我控制的速率从队列中消耗消息。我做了一些处理,然后尝试通过Datastax Java客户端写入Cassandra集群。我已使用maxRequestsPerConnection
和maxConnectionsPerHost
设置了我的Cassandra群集。但是,在测试中,我发现当我到达maxConnectionsPerHost
和maxRequestsPerConnection
时,session.executeAsync
的调用不会阻止。
我现在正在做的是使用new Semaphore(maxConnectionsPerHost * maxRequestsPerConnection)
并在每个异步请求之前递增它,并在executeAsync
返回的未来完成时递减它。这种方法效果很好,但由于驱动程序已在内部跟踪请求和连接,因此似乎是多余的。
有没有人想出更好的解决方案来解决这个问题?
有一点需要注意:我希望在完成之前将其视为未完成的请求。此包含重试!我从群集中获得可重试失败的情况(例如等待一致性的超时)是我想要反压并停止消耗队列消息的主要情况。
问题:
// the rate at which I consume messages depends on how fast this method returns
processMessage(message) {
// this appears to return immediately even if I have exhausted connections/requests
session.executeAsync(preparedStatement.bind(...));
}
目前的解决方案:
constructor() {
this.concurrentRequestsSemaphore = new Semaphore(maxConnectionsPerHost * maxRequestsPerConnection);
}
processMessage(message) {
ResultSetFuture resultSetFuture = session.executeAsync(preparedStatement.bind(...));
CompletableFuture<ResultSet> future = completableFromListenable(resultSetFuture);
concurrentRequestsSemaphore.acquireUninterruptibly();
future.whenComplete((result, exception) -> concurrentRequests.release());
}
此外,任何人都可以看到此解决方案存在任何明显问题吗?
答案 0 :(得分:4)
不杀死集群的一个可能的想法是&#34;节流&#34;您拨打executeAsync
的电话,例如在一批100(或任何数量最适合您的集群和工作负载)之后,您将在客户端代码中进行休眠并对所有100个期货进行阻塞调用(或使用Guava库来转换列表)将来的未来列表)
这样,在发出100个异步查询后,您将强制客户端应用程序等待所有这些查询成功,然后再继续执行。如果在调用future.get()
时发现任何异常,则可以安排重试。通常,Java驱动程序的默认RetryStrategy已尝试重试。
关于来自服务器的反压信号,从CQL二进制协议V3开始,有一个错误代码通知客户端协调器过载:https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v3.spec#L951
从客户端,您可以通过两种方式获取此重载信息:
答案 1 :(得分:2)
我现在正在做的是使用新的信号量(maxConnectionsPerHost * maxRequestsPerConnection)并在每个异步请求之前递增它,并在executeAsync返回的未来完成时递减它。这种方法效果很好,但由于驱动程序已在内部跟踪请求和连接,因此似乎是多余的。
这是一种非常合理的方法,允许新请求填写而其他请求完成。您可以将许可证发布到未来完成。
驱动程序本身不这样做的原因是它试图尽可能少地阻塞而不是快速失败。不幸的是,这会给客户带来一些责任。
在通常情况下,一次向主机发送那么多请求并不好。 C *具有native_transport_max_threads设置(默认值为128),用于控制一次处理请求的线程数。最好是在每个主机的2 *那个数字上限制自己。 (详见How Cassandra handle blocking execute statement in datastax java driver)
我希望在完成之前将其视为未完成的请求。这包括重试!我从群集中获得可重试失败的情况(例如等待一致性的超时)是我想要反压并停止消耗队列消息的主要情况。
在成功完成,耗尽重试或由于某种原因失败之前,驱动程序将无法完成未来。因此,您可以绑定释放信号量许可证,直到将来完成或失败。