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

时间:2014-08-01 18:38:31

标签: websocket signalr

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

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

此外,客户端第一次连接到服务器时,它总是很好地接收整个序列。它是表现出常规跳过问题的后续连接。因此,它似乎是服务器端问题,而不是客户端问题。

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

最后,我检查了丢失的消息是否可能只是按顺序交付(我可以忍受,尽管我假设SignalR提供了有序交付保证)。他们不是 - 消息刚刚消失在虚空中。

如果有帮助,我目前正在本地运行,在Win 8.1 x64上运行IIS Express并在IE Developer Channel和Chrome 36上进行测试。连接使用WebSockets。我无法在SignalR源(客户端或服务器)或Rx.Net源中找到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();
    }
}

1 个答案:

答案 0 :(得分:3)

您遇到的问题似乎与消息缓冲区溢出一致。当SignalR从其缓冲区释放消息时,默认情况下会在250个消息片段中执行此操作。

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

虽然您可以增加DefaultMessageBufferSize,但这可能无法解决您的根问题。您似乎正在尝试以比服务器将其发送到客户端更快的速度发送消息。如果连续执行此操作,无论大小如何,都将耗尽缓冲区空间。

它更常见于reduce the DefaultMessageBufferSize而不是增加它,因为缓冲区会消耗大量内存,尤其是当您向许多不同的客户端发送大量独特的大量消息时。

避免超越缓冲区的最佳方法是让客户端至少每1000条消息发送一次ACK。鉴于此,可以避免发送超过1000条未经处理的消息,从而完全避免这个问题。

顺便说一句,如果您有这种倾向,可以亲自看看SignalR's message buffer implementation。请注意,capacity构造函数参数是DefaultMessageBufferSize。

相关问题