我有一个简单的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();
}
}
答案 0 :(得分:3)
您遇到的问题似乎与消息缓冲区溢出一致。当SignalR从其缓冲区释放消息时,默认情况下会在250个消息片段中执行此操作。
SignalR将至少缓冲发送给给定connectionId
的最后1000条消息。这意味着当您发送第1251条消息时,前250个消息将被缓冲区取消引用。这解释了为什么当客户端第一个连接到服务器时,它会接收整个消息序列。在缓冲区丢弃片段之前,您必须向给定客户端发送至少1251条消息。同样,这都是假设默认设置。
虽然您可以增加DefaultMessageBufferSize,但这可能无法解决您的根问题。您似乎正在尝试以比服务器将其发送到客户端更快的速度发送消息。如果连续执行此操作,无论大小如何,都将耗尽缓冲区空间。
它更常见于reduce the DefaultMessageBufferSize而不是增加它,因为缓冲区会消耗大量内存,尤其是当您向许多不同的客户端发送大量独特的大量消息时。
避免超越缓冲区的最佳方法是让客户端至少每1000条消息发送一次ACK。鉴于此,可以避免发送超过1000条未经处理的消息,从而完全避免这个问题。
顺便说一句,如果您有这种倾向,可以亲自看看SignalR's message buffer implementation。请注意,capacity构造函数参数是DefaultMessageBufferSize。