我编写了一个服务,使用PollingDuplexHttpBinding
它有一个使用它的 Silverllight 客户端。我的服务基本上有给定数量的客户端向服务发送他们的数据(经常是每秒,并且数据非常大,每次调用大约 5KB),并监听其他客户端向服务发送的新数据被路由到他们,非常类似于聊天室架构。
我注意到的问题是,当客户端通过 Internet 连接到服务时,几分钟后服务的响应变得缓慢并且回复变得滞后。我得出的结论是,当服务主机的上传容量达到时(互联网上传速度,服务器上只有大约 15KB/s),其他客户端发送的消息会在有可用带宽时进行相应的缓冲和处理。我想知道如何才能限制服务用于存储从客户端接收到的消息的缓冲区的占用?我的客户获得所有数据并不是那么重要,而是他们获得其他人发送的最新数据,因此实时连接是我正在寻找的,以保证交付为代价。
简而言之,我希望能够在服务填满时清理我的队列/缓冲区,或者达到某个上限并开始用收到的呼叫再次填充它以消除延迟。我该怎么做呢?MaxBufferSize
我需要在服务端和客户端减少该属性吗?或者我是否需要在我的服务中编写此功能?有任何想法吗?
谢谢。
编辑:
这是我的服务架构:
//the service
[ServiceContract(Namespace = "", CallbackContract = typeof(INewsNotification))]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
public class NewsService
{
private static Dictionary<IChatNotification, string> clients = new Dictionary<IChatNotification, string>();
private ReaderWriterLockSlim subscribersLock = new ReaderWriterLockSlim();
[OperationContract(IsOneWay = true)]
public void PublishNotifications(byte[] data)
{
try
{
subscribersLock.EnterReadLock();
List<INewsNotification> removeList = new List<INewsNotification>();
lock (clients)
{
foreach (var subscriber in clients)
{
if (OperationContext.Current.GetCallbackChannel<IChatNotification>() == subscriber.Key)
{
continue;
}
try
{
subscriber.Key.BeginOnNotificationSend(data, GetCurrentUser(), onNotifyCompletedNotificationSend, subscriber.Key);
}
catch (CommunicationObjectAbortedException)
{
removeList.Add(subscriber.Key);
}
catch (CommunicationException)
{
removeList.Add(subscriber.Key);
}
catch (ObjectDisposedException)
{
removeList.Add(subscriber.Key);
}
}
}
foreach (var item in removeList)
{
clients.Remove(item);
}
}
finally
{
subscribersLock.ExitReadLock();
}
}
}
//the callback contract
[ServiceContract]
public interface INewsNotification
{
[OperationContract(IsOneWay = true, AsyncPattern = true)]
IAsyncResult BeginOnNotificationSend(byte[] data, string username, AsyncCallback callback, object asyncState);
void EndOnNotificationSend(IAsyncResult result);
}
服务配置:
<system.serviceModel>
<extensions>
<bindingExtensions>
<add name="pollingDuplex" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceThrottling maxConcurrentSessions="2147483647" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<pollingDuplex>
<binding name="myPollingDuplex" duplexMode="SingleMessagePerPoll"
maxOutputDelay="00:00:00" inactivityTimeout="02:00:00"
serverPollTimeout="00:55:00" sendTimeout="02:00:00" openTimeout="02:00:00"
maxBufferSize="10000" maxReceivedMessageSize="10000" maxBufferPoolSize="1000"/>
</pollingDuplex>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="NewsNotificationService.Web.NewsService">
<endpoint address="" binding="pollingDuplex" bindingConfiguration="myPollingDuplex" contract="NewsNotificationService.Web.NewsService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
<system.webServer>
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
客户端通常会在 500 毫秒到 1000 毫秒之间调用服务,如下所示:
_client.PublishNotificationAsync(byte[] data);
并且回调将通知客户端其他客户端发送的通知:
void client_NotifyNewsReceived(object sender, NewsServiceProxy.OnNewsSendReceivedEventArgs e)
{
e.Usernamer//WHich client published the data
e.data//contents of the notification
}
回顾一下,当客户端数量增加,并且服务主机通过互联网上传速度受到限制时,服务发送给订阅者的消息会在某处缓冲并在队列中处理,这就是导致问题的原因,我不知道这些消息在哪里缓冲。在 LAN 中,该服务运行良好,因为服务器的上传速度等于其下载速度(对于 100KB/s 的传入呼叫,它发出 100KB/s 的通知)。这些消息在哪里缓冲?我怎样才能清除这个缓冲区?
我做了一些实验来尝试查看消息是否在服务中缓冲,我尝试在客户端上调用此方法,但它始终返回 0,即使一个客户端仍在接收其他人发送的通知 4- 5 分钟前:
[OperationContract(IsOneWay = false)]
public int GetQueuedMessages()
{
return OperationContext.Current.OutgoingMessageHeaders.Count();
}