Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
380 views
in Technique[技术] by (71.8m points)

c# - Different forms of the WCF service contract interface

It appears I can freely switch between the following three different versions of the same WCF contract interface API, without breaking the clients:

[ServiceContract]
interface IService
{
    // Either synchronous
    // [OperationContract]
    // int SomeMethod(int arg);

    // Or TAP
    [OperationContract]
    Task<int> SomeMethodAsync(int arg);

    // Or APM
    // [OperationContract(AsyncPattern = true)]
    // IAsyncResult BeginSomeMethod(int arg, AsyncCallback callback, object state);
    // int EndSomeMethod(IAsyncResult ar);
}

The existing test client app keeps working without any recompiling or touching. If I do recompile the service and re-import its reference into the client app, the WSDL definition remains the same, 1:1.

My questions:

  • Is it a legit behavior I can rely on? Is it documented anywhere?

The idea is to convert a set of synchronous SomeMethod-style methods into TAP SomeMethodAsync-style methods, to use async/await in their implementation and thus improve the WCF service scalability, without breaking existing clients.

Also, there have been known woes with WCF service scaling under .NET 3.5 and .NET 4.0. They are documented in the MSKB article "WCF service may scale up slowly under load" and the CodeProject article "Tweaking WCF to build highly scalable async REST API". Basically, it wasn't enough to implement the service contract APIs as naturally asynchronous, the WCF runtime still was blocking the request thread.

  • Does anyone know if this problem has been fixed for .NET 4.5.x, out-of-the-box? Or the additional tweaks are still required?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

WCF operations can be defined using either synchronous, EAP or (as of .NET 4.5) TAP. From MSDN:

Clients can offer the developer any programming model they choose, so long as the underlying message exchange pattern is observed. So, too, can services implement operations in any manner, so long as the specified message pattern is observed.

You can actually have all 3 patterns in a single contract interface, and they would all relate to the same message.

On the wire, there's no difference how you execute the operations. WSDL (which WCF builds from each endpoint's ABC - address, binding and contract) doesn't contain this information. It is generated from operation descriptions.

If you look at the OperationDescription class, which is used in a ContractDescription, you'll see each operation has these properties: SyncMethod, BeginMethod, EndMethod and TaskMethod. When creating a description, WCF will combine all the methods according to the operation's name into a single operation. If there's some mismatch between operations with the same name in different patterns (e.g. different parameters) WCF would throw an exception detailing exactly what's wrong. WCF automatically assumes (optional) "Async" suffix for Task-based methods, and Begin/End prefix for APM.

The client and server side are completely unrelated in this sense. The utility that generates proxy classes from WSDL (svcutil), can build proxies for any execution pattern. It doesn't even have to be a WCF service.

On the server side, if more than one pattern is implemented, WCF will use just one in the following order of precedence: Task, Sync and APM. This is documented somewhere in MSDN, I just can't find it right now. But you can look at the reference source here.

In conclusion, you can safely change your server implementation as long as you don't modify the message the operation represents.

Regarding the scaling (should be a different question IMO)

  • WCF's throttling default values have been updated in .NET 4.5 to much more reasonable values and are now processor-dependent (see here).
  • There's no change in regards to the thread-pool issue. The problem stems from the initial size of the completion port thread-pool, which is initially set to 4 times the amount of the logical processors. You can use ThreadPool.SetMinThreads to increase the amount by some factor (see this post). This setting could also be beneficial on the client side.

If you use async on the server side (when calling other services, database, etc.), the threading situation could improve dramatically because you won't be wasting thread-pool threads that are just waiting for IO to complete.

The best thing in these situations is to do a LOT of benchmarking.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...