SignalR客户端定期错过一些事件



我有一个简单的SignalR设置:owin托管的。net服务器和JavaScript客户端(都@ v2.1.1)。客户端使用SignalR同步其在服务器上的Rx ReplaySubject中维护的有序事件流的副本。当客户端连接时,它提供一个startAfter查询参数,用于初始化针对ReplaySubjectIObserver,然后这个观察者将观察到的序列中的每个事件发送给客户端。每个事件都有一个序列号,客户端可以根据事件序列号判断序列中是否缺少任何事件。(这在这个应用程序中将是一个严重的问题。)

问题是客户端通常只接收到事件序列的一部分。事实上,这是有规律的。每250个事件有很大的差距。例如,每个测试显示第一个差距介于70和80到250之间。为什么总是250?从那以后,"skip-to"点的间隔总是250;例如,从263到500的差距,然后从511到750的差距,等等。我必须假设这是某种默认的缓冲区大小。

同样,客户端第一次连接到服务器时,它总是接收到整个序列。正是随后的连接表现出了常规的跳跃问题。因此,这似乎是一个服务器端的问题,而不是客户端的问题。

然后,我向服务器添加了一些检查,以确保每个客户机的IObserver以正确的顺序看到所有事件。它是。因此,几乎可以肯定的是,问题是在SignalR服务器端,与Rx无关。

最后,我检查了被丢弃的消息是否只是无序交付(我可以接受,尽管我假设SignalR提供了有序交付保证)。它们不是——消息只是消失在虚空中。

如果它有帮助,我目前在本地运行,在win8.1 x64上使用IIS Express,并在IE Developer Channel和Chrome 36上进行测试。该连接使用WebSockets。我在SignalR源(客户端或服务器)或Rx中找不到250作为特殊数量的任何参考。净源。

对故障排除有什么建议吗?我想在开始构建复杂的变通方案之前找到一个稳定的解决方案。

以下是相关的服务器端代码:
public class AllEventsReplaySource
{
    private readonly IHubConnectionContext<dynamic> clients;
    private readonly ReplaySubject<dynamic> allEvents;
    private AllEventsReplaySource(IHubConnectionContext<dynamic> clients)
    {
        this.clients = clients;
        this.allEvents = new ReplaySubject<dynamic>();
        // (Not shown: code that generates the input to the ReplaySubject.)
    }
    public void SubscribeClient(string connectionId, int startAfter)
    {
        this.allEvents.Skip(startAfter).Subscribe(e =>
        {
            // (Not shown: code that verifies no skips are occurring at this point for a client.)
            clients.Client(connectionId).notifyEvent(e);
        });
    }
    private readonly static Lazy<AllEventsReplaySource> instance =
        new Lazy<AllEventsReplaySource>(() => new AllEventsReplaySource(
            GlobalHost.ConnectionManager.GetHubContext<AllEventsReplayHub>().Clients));
    public static AllEventsReplaySource Instance
    {
        get { return instance.Value; }
    }
}
[HubName("allEventsReplayHub")]
public class AllEventsReplayHub : Hub
{
    private readonly AllEventsReplaySource source;
    public AllEventsReplayHub()
        : this(AllEventsReplaySource.Instance)
    { }
    public AllEventsReplayHub(AllEventsReplaySource source)
    {
        this.source = source;
    }
    public override Task OnConnected()
    {
        var previousSequenceNumber = Int32.Parse(Context.QueryString["startAfter"]);
        var connectionId = this.Context.ConnectionId;
        AllEventsReplaySource.Instance.SubscribeClient(connectionId, previousSequenceNumber);
        return base.OnConnected();
    }
}

您遇到的问题似乎与消息缓冲区溢出一致。当SignalR从它的缓冲区中释放消息时,默认情况下它以250个消息片段的形式释放消息。

SignalR将缓冲发送到给定connectionId的至少最后1000条消息。这意味着当您发送第1251条消息时,前250条消息将被缓冲区取消引用。这解释了为什么当客户机第一次连接到服务器时,它会接收整个消息序列。在缓冲区丢弃片段之前,您必须向给定的客户端发送至少1251条消息。同样,这些都是假设默认设置的。

虽然您可以增加DefaultMessageBufferSize,但这可能不会解决您的根本问题。似乎您试图发送消息的速度比服务器发送到客户端的速度要快。如果您连续这样做,无论大小,都将耗尽缓冲区空间。

减少DefaultMessageBufferSize比增加DefaultMessageBufferSize更常见,因为缓冲区会消耗大量内存,特别是当您向许多不同的客户端发送大量唯一的大消息时。

避免缓冲区溢出的最佳方法是让客户端至少每1000条消息发送一次ACK。考虑到这一点,也许可以避免发送超过1000条未标记的消息,从而完全避免这个问题。

顺便说一下,如果你有兴趣的话,你可以自己看看SignalR的消息缓冲区实现。请注意,容量构造函数参数是DefaultMessageBufferSize。

相关内容

  • 没有找到相关文章

最新更新