【问题标题】:Websocket singleton who uses Scoped Service使用 Scoped Service 的 Websocket 单例
【发布时间】:2019-10-14 12:11:25
【问题描述】:

我已经实现了 WebSocket 中间件,它包含一个字段单例 WebSocket 字典(通过构造函数注入)和其他一些通过构造函数注入的范围参数。

我想知道它是否正确实施。

public WebSocketManagerMiddleware(RequestDelegate next,
    IWebSocketConnectionDictionary webSocketDictionary,
    IScopedServiceOne scopedOneService, IScopedServiceTwo scopedtwoService)
{
    _next = next;
    _webSocketManager = webSocketDictionary;
    _scopedOneService= scopedOneService;
    _scopedtwoService= scopedtwoService;
}

对于这个构造函数,我像这样注入这些实例:

_app.UseMiddleware<WebSocketManagerMiddleware>(
    app.ApplicationServices.GetWebSocketConnectionDictionary(),
    serviceProvider.CreateScope().ServiceProvider.GetScopedOneService(),
    serviceProvider.CreateScope().ServiceProvider.GetScopedTwoService())

恐怕我每次在 WebSocket 请求上都会创建新的范围,我从中获得范围服务(serviceOne,serviceTwo),并且在 WebSocket 连接关闭之前它从未被释放。因为我只在 websocket 启动时使用这些服务,并且在我开始收听即将到来的消息之后我从不使用它们(IScopedOneSerice,IScopedTwoService)

public async Task Invoke(HttpContext context, IServiceProvider service)
{
    await _scopedOneService.MethodOne();
    await _scopedTwoService.MethodTwo();
    //startint to listen for messages and if I need to call some repository 
    // method I am using 
    //IServiceProvider, i.e ISomeRepository repo =
    //    service.GetRequiredService<ISomeRepository>(); // this repo scoped as well
}

这样有可能导致内存泄漏吗?

更新: 我想要实现的目标:让我们简单一点,每次收到 websocket 消息时,我都需要将消息插入存储库或解析与其他业务逻辑服务通信的其他一些服务。

我不确定将作用域服务注入包含单例 websocket 字典和其他一些作用域服务的 websocket 中间件的最佳方法是什么。

【问题讨论】:

  • 我认为您应该使用注入的IServiceScopeFactory 解决中间件内部的范围服务,而不是尝试从外部获取它们。将构造函数中的作用域服务替换为单个 IServiceScopeFactory 并存储它。每次您必须使用其中一个作用域服务时,通过创建一个新作用域并从那里解析它们来获取它们。
  • 我应该将作用域服务与 using() 一起使用,以便在使用后可以轻松处理吗?
  • 您能说明一下您要达到的目标(逐步流程)吗?
  • 好的,我会更新我的帖子
  • 1. WebSocket 客户端连接到我的中间件。 2. 添加一个新的 Websocket 到 Dictionary。 3、使用ScopedServiceOne、ScopedServiceTwo验证WebSocket连接是否正确。 4. 如果验证通过,那么我开始收听新消息 5. 新消息到达我解决了一些范围方法,该方法从消息中计算数据并插入数据库 6. 发送对消息的响应

标签: c# dependency-injection websocket .net-core middleware


【解决方案1】:

正如我在评论中所说,您应该注入 IServiceScopeFactory 而不是作用域服务。然后,您可以使用它来解析范围内的服务。

public WebSocketManagerMiddleware(RequestDelegate next,
    IWebSocketConnectionDictionary webSocketDictionary,
    IServiceScopeFactory scopeFactory)
{
    _next = next;
    _webSocketManager = webSocketDictionary;
    _scopeFactory = scopeFactory;
}

然后,您可以创建一个新范围并在需要访问它们时从那里解析服务。

using (var scope = _scopeFactory.CreateScope())
{
    var scopedOneService = scope.ServiceProvider.GetRequiredService<IScopedServiceOne>();
    var scopedTwoService = scope.ServiceProvider.GetRequiredService<IScopedServiceTwo>();
    // do something with scoped services
}

您还应该阅读this answer,这在这种情况下是正确的,但在 Chris 的回答所针对的问题的上下文中却不是,这就是它被否决的原因:)

【讨论】:

  • '超时。在从池中获取连接之前超时时间已过。这可能是因为所有池连接都在使用中并且达到了最大池大小。当 WebSocket 客户端每 10 毫秒发送一条消息时,我在进行负载测试时收到此错误。每条到达的消息都会对我的数据库调用 INSERT 查询。这是否意味着我有地方泄漏?开始负载测试 5 分钟后出现错误。从诊断工具中,我看到我的内存稳步增加,直到 600mb 并且 CPU 使用率为 100%
  • 听起来不太好。我可以向您保证,简单地使用来自IServiceScopeFactory 的范围解析服务不会使用那么多 CPU 和内存。我假设您没有正确处理您的套接字和连接。泄漏不在我向您展示的代码中。您应该尝试调试应用程序并逐步执行使用范围的方法以查看实际发生的情况。除了正确处理您打开的所有套接字和连接之外,我不能告诉您更多。还要研究你使用的类,也许你应该使用一个而不是像HttpClient这样的多个。
  • 我看到没有 IServiceScopeFactory 也有同样的效果。当然,这并不是所有的原因。我试图从我的 WebSocket 服务逻辑中删除所有数据库存储库查询。没有它们,我认为效果会更好。我正在使用 dapper,如果连接已关闭或断开,我将打开一个连接。SqlConnection 我作为作用域使用(在 DI 容器中注册)。所以我不处理它,因为它是作用域和重用的。但是,它仍然可能会产生泄漏
  • 我现在在本地计算机上所做的一切。也许对于我的 SQL 服务器来说,每 10 毫秒 3 次一次向我的数据库插入查询太多了,没有可用的连接,这就是它发生的原因?也许我对这种负载的 SQL 服务器期望过高?每 100 毫秒工作得更好,但所有查询处理时间肯定更长。
  • 您应该在批量添加消息之前缓冲这些消息。如果每 10 毫秒收到新消息,则应考虑将它们缓冲在队列中,然后每秒左右批量添加它们。它仍然有很多条目,如果你让这个系统运行很长时间,你可能会用完主键,这取决于你为它们使用的类型。不过,我认为您应该考虑在 RAM 中缓冲它们,批量添加它们,释放所有资源并重复。实际的消息应该只排队。实际的 DB 部分应该只从该队列中出列。没有直接连接!
猜你喜欢
  • 1970-01-01
  • 2019-12-25
  • 2022-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-16
相关资源
最近更新 更多