SignalR核心-使无效连接无效

时间:2019-02-06 22:11:52

标签: c# asp.net-core asp.net-core-signalr

问题

我正在将.NET Core 2.2与ASP.NET Core SignalR一起使用。当前,我将所有连接状态保存在SQL数据库中(请参阅this document;即使它是“旧” SignalR库的手册,其逻辑也相同)。我还使用Redis背板,因为我的应用程序可以水平缩放。

但是,当重新启动我的应用程序时,当前的连接不会关闭并且会变得孤立。先前链接的文章指出:

  

如果您的Web服务器停止工作或应用程序重新启动,则   不调用OnDisconnected方法。因此,有可能   您的数据存储库将具有不为连接ID的记录   有效期更长。要清理这些孤立的记录,您可能希望   使在时间范围之外创建的任何连接无效   与您的应用有关。

问题

在“旧的” SignalR中,有一个ITransportHeartbeat(完美实现了this script),但是.NET Core版本没有这样的接口(至少,我找不到它)。 / p>

我如何知道连接是否不再有效?我想要(或实际上需要)清理旧的连接ID。

2 个答案:

答案 0 :(得分:2)

我想出的解决方案如下。它不那么优雅,但是现在我看不到其他选择。

我更新了数据库中的模型,使其不仅包含ConnectionId,而且包含LastPing(这是DateTime类型)。客户端发送一条KeepAlive消息(自定义消息,不使用SignalR keepalive设置)。收到消息(服务器端)后,我将使用当前时间更新数据库:

var connection = _context.Connection.FirstOrDefault(x => x.Id == Context.ConnectionId);
connection.LastPing = DateTime.UtcNow;

要清理孤立的连接(SignalR的OnDisconnected方法未将其删除),我有一个定期运行的任务(当前在Hangfire中),该任务删除了LastPing字段尚未被删除的连接最近更新。

答案 1 :(得分:1)

带有 SignalR 的 .NET Core 2.1 具有 IConnectionHeartbeatFeature,您可以使用它来实现与旧 SignalR 中的 ITransportHeartbeat 类似的功能。

public class MyHub : Hub
{
    private readonly IConnectionHeartbeatFeature _heartbeat;
    private readonly MyDbContext context;
    
    public MyHub(MyDbContext context, IConnectionHeartbeatFeature heartbeat)
    {
        _context = context;
        _heartbeat = heartbeat;
    }

    public override Task OnConnectedAsync()
    {
        // Set up a new handler each time a new client connects.
        _heartbeat.OnHeartbeat(state => {
            (var context, var connectionId) = ((HttpContext, string))state;
    
            // Retrieve required dependencies.
            var dbContext = httpContext.RequestServices.GetService<MyDbContext>();
    
            // Retrieve persistent connection state recorded in the database.
            var connection = dbContext.Connection.FirstOrDefault(x => x.Id == Context.ConnectionId);

            // Don't attempt to update this connection if it has recently been updated.
            // OnHeartbeat is called an estimated (currently non-configurable) once 
            // every second per connection. You don't want to overload your server
            // or cause locking issues when there are many clients connected.
            var now = DateTime.UtcNow;
            if (connection == null || (now - connection.LastPing).TotalSeconds < 10)
                return;

            // Record the last time this connection was updated.
            connection.LastPing = DateTime.UtcNow;

            // Save changes to the database.
            dbContext.SaveChanges();
        }, (Context.GetHttpContext(), Context.ConnectionId))
    }

    // ...
}

您仍然需要一个后台任务(例如:HostedService),它会定期运行以根据@Devator 的回答查找和清理陈旧的连接。

如果您使用的是 Azure SignalR 服务,则与手动发送 KeepAlive 消息相比,这种方法的优势在于您无需为该消息付费(因为 OnHeartbeat 发生在内部)。

请记住,此功能根本没有真正记录在案。我不确定这在野外的测试情况如何。