I wrote a service using PollingDuplexHttpBinding
, which has a Silverllight client that uses it. My Service basically has a certain number of clients sending their data (often every second, and the data is quite large, each call is about 5 KB) for this service, as well as listening to new data sent by other clients to the service to be sent to them, itβs very similar to chat room architecture.
The problem that I notice is that when clients connect to the service via the Internet, after a few minutes the response of the service slows down and the responses become lagging. I came to the conclusion that when the bandwidth of the service host is reached (Internet download speed on the server is about 15 KB / s), messages sent by other clients are buffered and processed accordingly when there is available bandwidth. I am wondering how can I limit the capture of this buffer that the service uses to store received messages from clients? It is not so critical that my clients receive all the data, but rather receive the latest data sent by others, so a real-time connection is what I am looking for due to guaranteed delivery.
In short, I want to be able to clear the queue / buffer in the service whenever it is full, or a certain cap is reached, and again fill it with received calls to get rid of the delay. How can I do it? Is the MaxBufferSize
property what I need to reduce on the service side as well as the client side? Or do I need to encode this function in my service? Any ideas?
Thanks.
EDIT:
Here is my service architecture:
//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); }
Service Configuration:
<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>
The client will usually call the service between periods from 500ms to 1000ms, for example:
_client.PublishNotificationAsync(byte[] data);
and the callback will notify the client of notifications sent by other clients:
void client_NotifyNewsReceived(object sender, NewsServiceProxy.OnNewsSendReceivedEventArgs e) { e.Usernamer
Thus, when the number of clients increases, and the speed of loading the host nodes of the service on the Internet is limited, messages sent by the service to subscribers receive a buffer somewhere and are processed in the queue, which is the reason for the problem, I do not know where these messages are buffered. The service works fine on the local network because the server has a download speed equal to its download speed (for 100 KB / s incoming calls, it sends 100 KB / s notifications). Where are these messages buffered? And how can I clear this buffer?
I did something experimental to try to check if messages are being buffered in the service, I tried calling this method on the client, but it always returns 0, even when one client is still in the process of receiving notifications that someone else shipped 4-5 minutes ago:
[OperationContract(IsOneWay = false)] public int GetQueuedMessages() { return OperationContext.Current.OutgoingMessageHeaders.Count(); }