簡體   English   中英

如何清除服務器上的傳出消息緩沖區?

[英]How to clear the outgoing message buffers at the Server?

我使用PollingDuplexHttpBinding編寫了一個服務,它有一個消耗它的Silverllight客戶端。 我的服務基本上有一定數量的客戶端向服務發送數據(通常,每秒一次,數據非常大,每次調用大約5KB),並且還監聽其他客戶端發送給服務的新數據。被路由到他們,非常類似於聊天室架構。

我注意到的問題是,當客戶端通過互聯網連接到服務時,幾分鍾后服務的響應變慢,回復變得滯后。 我得出的結論是,當服務主機上傳容量達到(互聯網上傳速度,在服務器上僅約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
        }

因此,回顧一下,當客戶數量增加,服務主機上傳速度超過互聯網時,服務發送給訂戶的消息會在某處緩沖並在隊列中處理,這是導致問題的原因,我不知道這些消息在哪里被緩沖。 在局域網中,服務工作正常,因為服務器的上傳速度等於其下載速度(對於100KB / s的傳入呼叫,它發出100KB / s的通知)。 這些消息在哪里被緩沖? 我該如何清除這個緩沖區呢?

我做了一些實驗性嘗試,看看消息是否在服務上緩沖,我嘗試在客戶端調用此方法,但它總是返回0,即使一個客戶端仍在接收其他人發送的通知4 5分鍾前:

[OperationContract(IsOneWay = false)]
public int GetQueuedMessages()
{

            return OperationContext.Current.OutgoingMessageHeaders.Count();
}

我根據你的情況做了一些數學計算。

  • 郵件大小= 100KB
  • 上傳頻道= 15KB / s
  • 下載頻道= 100KB / s
  • 客戶端每秒呼叫服務1-2次

客戶端通常會在500毫秒到1000毫秒的時間段內調用該服務

它是否正確?

對於一個客戶端,僅針對消息的下載流量將為100-200KB / s,這僅是消息正文。 在啟用安全性的情況下,將有更多標題和更多內容。

消息將被組合用於異步調用。 因此,如果我們有3個客戶端並且每個發送一條消息,則回調包含每個客戶端的2條消息。 4個客戶端 - 每個回調中有3條消息。

3個客戶端的下載頻道為200-400KB / s。

對我來說,看起來消息對於您聲明的帶寬來說太大了。

檢查你是否可以:

  1. 減少郵件大小。 我不知道你的業務性質,所以不能在這里給出建議。

  2. 對郵件或流量使用壓縮。

  3. 增加網絡帶寬。 沒有它,即使理想的解決方案也會有很高的延遲。 您可以花費數天和數周來優化代碼,即使您正確使用網絡解決方案仍然會很慢。

我知道這聽起來像是Captain Obvious但是有些問題沒有正確答案,除非你改變問題。

執行上述步驟后,將ServiceThrottlingBehavior與將管理回調隊列的自定義代碼結合使用。

如果達到邊界, ServiceThrottlingBehavior拒絕請求。

http://msdn.microsoft.com/en-us/library/ms735114(v=vs.100).aspx

這是一個微不足道的樣本。 應根據您的環境專門定義實數。

<serviceThrottling 
 maxConcurrentCalls="1" 
 maxConcurrentSessions="1" 
 maxConcurrentInstances="1"
/>

更新:

我在這個問題上犯了大錯,每個電話都是5KB / s,

即使有5KB的消息,15KB / s還不夠。 我們只計算3個用戶的流量。 3每秒傳入消息。 系統現在應該使用雙工通知用戶其他人發送了什么。 每個用戶都應該從他的合作伙伴處獲得消 我們共有3條消息。 可以跳過一個(屬於發件人),因此每個用戶都應該收到2條消息。 3個用戶將獲得10KB(5KB + 5KB =一個消息10KB)= 30KB。 每秒一封郵件將為3個用戶上傳頻道提供30KB /秒的速度。

這些消息在哪里被緩沖? 我該如何清除這個緩沖區呢?

這取決於您如何托管您的服務。 如果是自托管服務,則根本不進行緩沖。 您的代碼嘗試向接收方發送消息,但由於通道泛濫,因此速度非常慢。 使用IIS可能會有一些緩沖,但從外部觸摸它並不是一個好習慣。

正確的解決方案是限制。 由於帶寬限制,您不應嘗試將所有消息發送到所有客戶端。 相反,您應該限制發送消息的線程數量。 因此,從上面的3個用戶而不是並行發送3個消息,您可以按順序發送它們,或者決定只有一個用戶將在該輪次中獲得更新,下一輪中的下一個用戶將獲得更新。

因此,一般的想法是,只要您擁有數據,就不會嘗試將所有內容發送給所有人,但只發送您可以承受的數據量,並使用線程數或響應速度來控制數據。

MaxBufferSize不會幫助您解決此問題。 您將不得不自己編寫代碼我不知道任何現有的解決方案/框架。 然而,它聽起來像一個有趣的問題。 您可以從為每個連接的客戶端維護Queue<Message>開始,當推送到此隊列時(或當客戶端調用出隊時),您可以重新評估應發送的Message

更新:首先,我會忘記嘗試從客戶端和配置中執行此操作,您將不得不自己編寫代碼

在這里,我可以看到您發送給客戶的位置:

 subscriber.Key.BeginOnNotificationSend(data, GetCurrentUser(), onNotifyCompletedNotificationSend, subscriber.Key);

因此,不應將這些通知異步推送到每個客戶端,而應將它們推送到Queue<byte[]> 每個客戶端連接都有自己的隊列,您可能應該構建一個專用於每個客戶端連接的類:

請注意,此代碼不會開箱即用,可能會出現一些邏輯錯誤,僅供參考

public class ClientConnection
{
    private INewsNotification _callback;
    private Queue<byte> _queue = new Queue<byte>();
    private object _lock = new object();
    private bool _isSending
    public ClientConnection(INewsNotification callBack)
    {
           _callback=callback;
    }
    public void Enqueue(byte[] message)
    { 
       lock(_lock)
       {               
           //what happens here depends on what you want to do. 
           //Do you want to only send the latest message? 
           //if yes, you can just clear out the queue and put the new message in there,                         
           //or you could keep the most recent 5 messages.                
           if(_queue.Count > 0)
               _queue.Clear();
          _queue.Enqueue(message);
           if(!_isSending)
               BeginSendMessage();
       }
    }

    private void BeginSendMessage()
    {
       _isSending=true;
        _callback.BeginOnNotificationSend(_queue.Dequeue(), GetCurrentUser(), EndCallbackClient, subscriber.Key);

    }

    private void EndCallbackClient(IAsyncResult ar)
    {
        _callback.EndReceive(ar);
        lock(_lock)
        {
           _isSending=false;
           if(_queue.Count > 0)
              BeginSendMessage();//more messages to send
        }
    }
}

想象一下這樣一種情況,即一條消息被推送到客戶端,而另外9條消息則調用ClientConnection.Enqueue 第一條消息完成后,它會檢查應該只包含最后一條消息的隊列(第9條消息)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM