如何使用事件存储数据库客户端而不持续内存使用增长?



我正在使用。net的事件存储客户端,我正在努力寻找正确的方法来使用客户端。当我在。net依赖注入中将客户端注册为单例并运行我的应用程序很长一段时间后,随着每次订阅,内存使用量不断增长。

我以以下方式创建并注册客户端。在这里可以找到一个遇到这个问题的完整的最小应用程序。

var esdbConnectionString = configuration.GetValue("ESDB_CONNECTION_STRING", "esdb://admin:changeit@localhost:2113?tls=false");
var eventStoreClientSettings = EventStoreClientSettings.Create(esdbConnectionString);
var eventStoreClient = new EventStoreClient(eventStoreClientSettings);
services.AddSingleton(eventStoreClient);

我的应用程序在很长一段时间内有大量的短流

<标题>

复制重现行为的步骤:

  1. 按照文档中的建议将EventStoreClient注册为单例。
  2. 在很长一段时间内订阅大量的流。
  3. 取消发送到流订阅的CancellationToken,让它被垃圾收集。
  4. 查看服务内存使用情况。

我是如何创建和订阅流的:

var streamName = CreateStreamName();
var payload = new PingEvent { StreamNr = _currentStreamNumber };
var eventData = new EventData(Uuid.NewUuid(), typeof(PingEvent).Name, EventSerialization.SerializeEventData(payload));
await _client.AppendToStreamAsync(streamName, StreamState.Any, new[] { eventData });
var streamCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(30));
await _client.SubscribeToStreamAsync(streamName, FromStream.Start, async (sub, evnt, token) =>
{
if (evnt.Event.EventType == "PongEvent")
{
_previousStreamIsDone = true;
streamCancellationTokenSource.Cancel();
}
},
cancellationToken: streamCancellationTokenSource.Token);
<标题>方法尝试h1> strong>注册为暂态或作用域如果我在。net DI中将客户端注册为Transient或Scoped,它会在内部抛出数千个异常并导致多个问题。

手动处理客户端生存期通过使用处理客户端生命周期的单例服务,我试图每隔一段时间就处理客户端并创建一个新客户端,以确保同时只存在一个客户端实例。这将导致与将服务注册为Transient或Scoped相同的问题。

我正在使用。net 6中的22.0.0版本的事件存储客户端对事件存储数据库21.10.0。该问题在windows和标准的asp.net:6.0 linux docker容器上运行时都会发生。

通过检查这些dotnet-dumps的结果,内存增长似乎发生在gRPC客户端的activeccall的HashSet中。

我希望找到一种使用客户端不会导致内存增长的方法。

在您的复制中,泄漏的调用来自您在处理订阅上收到的事件时发出的额外读取。

目前有一个开放的问题(https://github.com/EventStore/EventStore-Client-Dotnet/issues/219)来更好地处理这个问题,但目前如果您发出读取但不消耗所有事件并且不取消读取,则调用保持打开状态。在您的示例中,如果从服务器在主服务器发出读操作之前成功地回复了Pong,那么就会发生这种情况,因为在订阅中接收到自己的Ping。然后读取将包含Ping和Pong,只有Ping被读取,并且调用保持打开状态。

现在,如果你通过将取消令牌传递到ReadFromStartOfStreamToEnd中的ReadStreamAsync调用来取消这些读取,它应该可以解决你的问题。

如果它对您有帮助,您可以看到Current Calls的数量,而不是等待很长时间来查看对内存的影响:

dotnet-counters monitor --counters "Grpc.Net.Client" -p <processid>

最新更新