我正在将.NET Core 2.2与ASP.NET Core SignalR一起使用。当前,我将所有连接状态保存在SQL数据库中(请参阅this document;即使它是“旧” SignalR库的手册,其逻辑也相同)。我还使用Redis背板,因为我的应用程序可以水平缩放。
但是,当重新启动我的应用程序时,当前的连接不会关闭并且会变得孤立。先前链接的文章指出:
如果您的Web服务器停止工作或应用程序重新启动,则 不调用OnDisconnected方法。因此,有可能 您的数据存储库将具有不为连接ID的记录 有效期更长。要清理这些孤立的记录,您可能希望 使在时间范围之外创建的任何连接无效 与您的应用有关。
在“旧的” SignalR中,有一个ITransportHeartbeat
(完美实现了this script),但是.NET Core版本没有这样的接口(至少,我找不到它)。 / p>
我如何知道连接是否不再有效?我想要(或实际上需要)清理旧的连接ID。
答案 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 发生在内部)。
请记住,此功能根本没有真正记录在案。我不确定这在野外的测试情况如何。