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