Производительность, задержка и масштабируемость WCF

Я пытаюсь перенести простой асинхронный TCP-сервер в F # на C # 4. Сервер получает соединение, читает один запрос и возвращает последовательность ответов перед закрытием соединения.

Async в C # 4 выглядит утомительно и подвержено ошибкам, поэтому я решил попробовать использовать WCF. Этот сервер вряд ли увидит 1000 одновременных запросов в дикой природе, поэтому я думаю, что пропускная способность и задержка представляют интерес.

Я написал минимальный дуплексный веб-сервис WCF и консольный клиент на C #. Хотя я использую WCF вместо необработанных сокетов, это уже 175 строк кода по сравнению с 80 строками оригинала. Но меня больше беспокоит производительность и масштабируемость:

Задержка в 154 раза хуже с WCF.Пропускная способность в 54 раза хуже с WCF.TCP легко обрабатывает 1000 одновременных подключений, а WCF только 20.

Во-первых, я использую настройки по умолчанию для всего, поэтому мне интересно, можно ли что-нибудь изменить, чтобы улучшить эти показатели производительности?

Во-вторых, мне интересно, если кто-то использует WCF для такого рода вещей или это неправильный инструмент для работы?

Вот мой WCF-сервер в C #:

IService1.cs

[DataContract]
public class Stock
{
  [DataMember]
  public DateTime FirstDealDate { get; set; }
  [DataMember]
  public DateTime LastDealDate { get; set; }
  [DataMember]
  public DateTime StartDate { get; set; }
  [DataMember]
  public DateTime EndDate { get; set; }
  [DataMember]
  public decimal Open { get; set; }
  [DataMember]
  public decimal High { get; set; }
  [DataMember]
  public decimal Low { get; set; }
  [DataMember]
  public decimal Close { get; set; }
  [DataMember]
  public decimal VolumeWeightedPrice { get; set; }
  [DataMember]
  public decimal TotalQuantity { get; set; }
}

[ServiceContract(CallbackContract = typeof(IPutStock))]
public interface IStock
{
  [OperationContract]
  void GetStocks();
}

public interface IPutStock
{
  [OperationContract]
  void PutStock(Stock stock);
}

Service1.svc

<%@ ServiceHost Language="C#" Debug="true" Service="DuplexWcfService2.Stocks" CodeBehind="Service1.svc.cs" %>

Service1.svc.cs

 [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
 public class Stocks : IStock
 {
   IPutStock callback;

   #region IStock Members
   public void GetStocks()
   {
     callback = OperationContext.Current.GetCallbackChannel<IPutStock>();
     Stock st = null;
     st = new Stock
     {
       FirstDealDate = System.DateTime.Now,
       LastDealDate = System.DateTime.Now,
       StartDate = System.DateTime.Now,
       EndDate = System.DateTime.Now,
       Open = 495,
       High = 495,
       Low = 495,
       Close = 495,
       VolumeWeightedPrice = 495,
       TotalQuantity = 495
     };
     for (int i=0; i<1000; ++i)
       callback.PutStock(st);
   }
   #endregion
 }

Web.config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="DuplexWcfService2.Stocks">
        <endpoint address="" binding="wsDualHttpBinding" contract="DuplexWcfService2.IStock">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

Вот клиент C # WCF:

Program.cs

 [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
 class Callback : DuplexWcfService2.IStockCallback
 {
   System.Diagnostics.Stopwatch timer;
   int n;

   public Callback(System.Diagnostics.Stopwatch t)
   {
     timer = t;
     n = 0;
   }

   public void PutStock(DuplexWcfService2.Stock st)
   {
     ++n;
     if (n == 1)
       Console.WriteLine("First result in " + this.timer.Elapsed.TotalSeconds + "s");
     if (n == 1000)
       Console.WriteLine("1,000 results in " + this.timer.Elapsed.TotalSeconds + "s");
   }
 }

 class Program
 {
   static void Test(int i)
   {
     var timer = System.Diagnostics.Stopwatch.StartNew();
     var ctx = new InstanceContext(new Callback(timer));
     var proxy = new DuplexWcfService2.StockClient(ctx);
     proxy.GetStocks();
     Console.WriteLine(i + " connected");
   }

   static void Main(string[] args)
   {
     for (int i=0; i<10; ++i)
     {
       int j = i;
       new System.Threading.Thread(() => Test(j)).Start();
     }
   }
 }

Вот мой асинхронный TCP-клиент и серверный код на F #:

type AggregatedDeals =
  {
    FirstDealTime: System.DateTime
    LastDealTime: System.DateTime
    StartTime: System.DateTime
    EndTime: System.DateTime
    Open: decimal
    High: decimal
    Low: decimal
    Close: decimal
    VolumeWeightedPrice: decimal
    TotalQuantity: decimal
  }

let read (stream: System.IO.Stream) = async {
  let! header = stream.AsyncRead 4
  let length = System.BitConverter.ToInt32(header, 0)
  let! body = stream.AsyncRead length
  let fmt = System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
  use stream = new System.IO.MemoryStream(body)
  return fmt.Deserialize(stream)
}

let write (stream: System.IO.Stream) value = async {
  let body =
    let fmt = System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
    use stream = new System.IO.MemoryStream()
    fmt.Serialize(stream, value)
    stream.ToArray()
  let header = System.BitConverter.GetBytes body.Length
  do! stream.AsyncWrite header
  do! stream.AsyncWrite body
}

let endPoint = System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 4502)

let server() = async {
  let listener = System.Net.Sockets.TcpListener(endPoint)
  listener.Start()
  while true do
    let client = listener.AcceptTcpClient()
    async {
      use stream = client.GetStream()
      let! _ = stream.AsyncRead 1
      for i in 1..1000 do
        let aggregatedDeals =
          {
            FirstDealTime = System.DateTime.Now
            LastDealTime = System.DateTime.Now
            StartTime = System.DateTime.Now
            EndTime = System.DateTime.Now
            Open = 1m
            High = 1m
            Low = 1m
            Close = 1m
            VolumeWeightedPrice = 1m
            TotalQuantity = 1m
          }
        do! write stream aggregatedDeals
    } |> Async.Start
}

let client() = async {
  let timer = System.Diagnostics.Stopwatch.StartNew()
  use client = new System.Net.Sockets.TcpClient()
  client.Connect endPoint
  use stream = client.GetStream()
  do! stream.AsyncWrite [|0uy|]
  for i in 1..1000 do
    let! _ = read stream
    if i=1 then lock stdout (fun () ->
      printfn "First result in %fs" timer.Elapsed.TotalSeconds)
  lock stdout (fun () ->
    printfn "1,000 results in %fs" timer.Elapsed.TotalSeconds)
}

do
  server() |> Async.Start
  seq { for i in 1..100 -> client() }
  |> Async.Parallel
  |> Async.RunSynchronously
  |> ignore

Ответы на вопрос(3)

Ваш ответ на вопрос