Choosing a WCF Instance Management - wcf

We are designing a WCF web service on IIS with the following characteristics:
Object creation is relatively heavy (takes about 500 ms) because it involves writing a file
No state needs to be preserved once an object is created
Each call from a client takes on average of 150 - 200 ms. This call involves sending a UDP request to another server and receiving a response.
We expect about 30 simultaneous clients. It may grow to 50 clients. In the worst case scenario (50 clients), we will need a pool of 10 instances of the object to handle this load.
Which of the 3 Instance Management contexts (PerCall, PerSession, Single) would you recommend and why? Which instance enables us to manage a pool of available objects that would do the work?

Out of the box, WCF does not support a service object pool. You need a custom IInstanceProvider. In this scenario the WCF context mode would define when WCF requests a new object from the IInstanceProvider and the IInstanceProvider behavior would manage the pool. Setting the service to either PerInstance or PerSession could make sense depending on the usage.
If you are using a Dependency Injection Container in your implementation such as Castle Windsor, StructureMap, or MS Enterprise Library's Unity then you can use the container's exsiting IInstanceProvider with a pooled lifestyle. All of those containers are reasonable (although I don't personally have much experience with having them manage object pools).
My personal choice of container is Castle Windsor. In that case you would use Windsor's WCF Integration Facility and a pooled lifestyle.
Here's a quick test console program that uses the Castle.Facilites.WcfIntegraion NuGet package.
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Threading.Tasks;
namespace WCFWindsorPooledService
{
[ServiceContract]
public interface ISimple
{
[OperationContract]
void Default(string s);
}
public class SimpleService : ISimple
{
private static int CurrentIndex = 0;
private int myIndex;
public SimpleService()
{
// output which instance handled the call.
myIndex = System.Threading.Interlocked.Increment(ref CurrentIndex);
}
public void Default(string s)
{
Console.WriteLine("Called #" + myIndex);
System.Threading.Thread.Sleep(5);
}
}
class PooledService
{
static void Main(string[] args)
{
Console.WriteLine("\n\n" + System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name);
// define mapping of interfaces to implementation types in Windsor container.
IWindsorContainer container = new WindsorContainer();
container.AddFacility<WcfFacility>()
.Register(Component.For<SimpleService>().LifeStyle.PooledWithSize(2, 5));
var host = new Castle.Facilities.WcfIntegration.DefaultServiceHostFactory()
.CreateServiceHost(typeof(SimpleService).AssemblyQualifiedName,
new Uri[] { new Uri("http://localhost/Simple") });
host.Open();
ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(host.Description.Endpoints[0]);
List<Task> tasks = new List<Task>();
for (int i = 0; i < 20; i++)
{
tasks.Add(Task.Run(() =>
{
ISimple proxy = factory.CreateChannel();
proxy.Default("Hello");
((ICommunicationObject)proxy).Shutdown();
}));
}
Task.WaitAll(tasks.ToArray());
((ICommunicationObject)host).Shutdown();
container.Dispose();
}
}
public static class Extensions
{
static public void Shutdown(this ICommunicationObject obj)
{
try
{
obj.Close();
}
catch (Exception ex)
{
Console.WriteLine("Shutdown exception: {0}", ex.Message);
obj.Abort();
}
}
}
}
I'm not going to pretend to understand all the rules of how Castle manages a pool, but a pool is clearly being used. The output is:
PooledService
Called #1
Called #5
Called #2
Called #3
Called #4
Called #6
Called #7
Called #8
Called #7
Called #4
Called #2
Called #5
Called #1
Called #10
Called #6
Called #9
Called #4
Called #7
Called #1
Called #9

Related

Can I have per AppDomain Environment Variables in C#/.net?

In a multi appdomain setup, is there a way to make SetEnvironementVariables and Get.... work within the appdomain only, so each appdomain can have different values for the same variable?
No. :(
This example:
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var newDomain = AppDomain.CreateDomain("Alternative");
Proxy proxyObj = (Proxy)newDomain.CreateInstanceAndUnwrap(typeof(Proxy).Assembly.GetName().FullName,
typeof(Proxy).FullName);
Environment.SetEnvironmentVariable("HELLO_MSG", "Hello World", EnvironmentVariableTarget.Process);
proxyObj.ShowEnvironmentVariable();
Console.ReadKey();
}
}
class Proxy : MarshalByRefObject
{
public void ShowEnvironmentVariable()
{
var msg = Environment.GetEnvironmentVariable("HELLO_MSG");
Console.WriteLine(String.Format("{0} (from '{1}' AppDomain)", msg, AppDomain.CurrentDomain.FriendlyName));
}
}
}
Will output:
Hello World (from 'Alternative' AppDomain)
The process is the most specific level of encapsulation for environment variables, and AppDomains will still "live inside" the same process.
Note that this will happen for all other process-level information (such as Directory.GetCurrentDirectory(), command-line args, etc.
One possible solution would create worker processes (".exe" applications spawned from the main process), but that will certainly add some complexity to your application.

Wcf Singleton with Single Thread

Can someone explain the issues that although I set both the InstanceContextMode and ConcurrencyMode to single with max concurrent calls instances and sessions set to 1 in ServiceThrottlingBehavior, I still found that at least 2 threads are processing the wcf requests?
Client Output:
Client name :kk Instance:1 Thread:13 Time:2013/12/30 12:17:56
Client name :kk Instance:1 Thread:12 Time:2013/12/30 12:17:57
Client name :kk Instance:1 Thread:13 Time:2013/12/30 12:17:58
Server Code:
Uri httpUrl = new Uri("http://localhost:8010/MyService/HelloWorld");
//Create ServiceHost
ServiceHost host
= new ServiceHost(typeof(ClassLibrary1.HelloWorldService), httpUrl);
//Add a service endpoint
host.AddServiceEndpoint(typeof(ClassLibrary1.IHelloWorldService)
, new WSHttpBinding(), "");
//Enable metadata exchange
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 1,
MaxConcurrentInstances = 1 ,
MaxConcurrentSessions = 1
};
host.Description.Behaviors.Add(smb);
host.Description.Behaviors.Add(stb);
//Start the Service
host.Open();
Client Code:
ServiceReference1.HelloWorldServiceClient obj = new ServiceReference1.HelloWorldServiceClient();
for(int i=0;i<15;i++)
{
obj.Call(str);
Thread.Sleep(1000);
}
Service Code:
[ServiceContract]
public interface IHelloWorldService
{
[OperationContract(IsOneWay=true)]
void Call(string ClientName);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public class HelloWorldService : IHelloWorldService
{
public static int i;
public HelloWorldService()
{
i++;
}
public void Call(string ClientName)
{
Console.WriteLine("Client name :" + ClientName + " Instance:" + i.ToString() + " Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + " Time:" + DateTime.Now.ToString() + "\n\n");
}
}
I'm not an expert on threading, but I'll take a stab at this and expand upon my comments.
According to MSDN, ConcurrencyMode.Single means The service instance is single-threaded and does not accept reentrant calls. If the InstanceContextMode property is Single, and additional messages arrive while the instance services a call, these messages must wait until the service is available or until the messages time out.
You're calling your service with a 1 second delay between each successive call. Your output shows this as well - each call is 1 second later than the immediately previous one.
I believe the thread id is a red herring in this case, as I would expect the service to use an available thread from the thread pool. I don't see anything in the documentation that guarantees the same thread will be used every time, and it seems to me that would be an unreasonable expectation.
If you're expecting a dedicated thread from the available threads, I don't think you can do that. If you're expecting the service to handle only one request at a time, the way you have it should do that.
I agree with Tim's answer that same thread need not be servicing all the calls. ConcurencyMode.Single will only guarantee one thread is servicing the call at a time.
If for some reason you require thread affinity on your WCF service, for example, if you are running a singleton service on a WinForms/WPF application and you want the service calls to run over the UI thread only - then, you just have to Open the service host on the UI thread. WCF is SynchronizationContext aware and will dispatch calls to UI thread only irrespective of what your ConcurrencyMode is. Also, see UseSynchronizationContext property of ServiceBehavior attribute.

WCF Service and best practices surrounding clients and open/close methods

Having a WCF service and a Consumer I'm not really sure how to handle the Open and Close methods and the lifetime of my Client.
I created the client myself extending and implementing ClientBase and IMyService. Let's call it MyServiceClient
One place I use it for example is MembershipProvider. So I gave MembershipProvider a MyClient as member variable.
I would like to have it instanced once in the MembershipProvider (via IoC container) and then perhaps do a Open and Close call inside every method call in the client.
public bool ValidateUser(string username, string password)
{
this.Open();
bool b = Channel.ValidateUser(username, password);
this.Close();
return b;
}
Is this the right way to go about it. I don't really understand what's really happening when open/close is called and how having one instance of client affects the service (if at all).
One of the problems with using a single client (WCF proxy) instance is that when a fault occurs the proxy enters a faulted state, and it cannot be reused or Dispose-d, only Abort-ed and created anew. On the other hand, if you use/require Sessions on the service side you need the same proxy instance across multiple calls.
In any case, if you would like to use proxy now and worry about transport, sessions or faults later I suggest a wrapper like this that I use for my WCF proxies:
TResult ExecuteServiceMethod<TResult>(Func<MyService, TResult> method)
{
var proxy = new MyService(); //...Or reuse an existing if not faulted
try
{
return method(proxy);
}
catch(Exception e)
{
//...Handle exceptions
}
finally
{
//...Per-call cleanup, for example proxy.Abort() if faulted...
}
}
and you call your service methods like this:
var result = ExecuteServiceMethod((MyService s) => s.VerifyUser(username, password));
Replace MyService with your actual client type. This way you can later change your opening/closing/reusing strategy, add logging, etc. for all service calls by adding code before or after the line return method(client).

NserviceBus. How to start several buses in different AppDomains?

I want to have several buses in one process. I googled about this and found that it is possible only if having several AppDomains. But I cannot make it work.
Here is my code sample (I do everything in one class library):
using System;
using System.Diagnostics;
using System.Reflection;
using MyMessages;
using NServiceBus;
using NServiceBus.Config;
using NServiceBus.Config.ConfigurationSource;
namespace Subscriber1
{
public class Sender
{
public static void Main()
{
var domain = AppDomain.CreateDomain("someDomain", AppDomain.CurrentDomain.Evidence);
domain.Load(Assembly.GetExecutingAssembly().GetName());
domain.CreateInstance(Assembly.GetExecutingAssembly().FullName, typeof (PluginBusCreator).FullName);
//here I have some code to send messages to "PluginQueue".
}
}
public class PluginBusCreator
{
public PluginBusCreator()
{
var Bus = Configure.With(
Assembly.Load("NServiceBus"), Assembly.Load("NServiceBus.Core"),
Assembly.LoadFrom("NServiceBus.Host.exe"), Assembly.GetCallingAssembly())
.CustomConfigurationSource(new PluginConfigurationSource())
.SpringFrameworkBuilder()
.XmlSerializer().MsmqTransport()
.UnicastBus().LoadMessageHandlers<First<SomeHandler>>().CreateBus().Start();
}
protected IBus Bus { get; set; }
}
class PluginConfigurationSource : IConfigurationSource
{
public T GetConfiguration<T>() where T : class
{
{
if (typeof (T) == typeof (MsmqTransportConfig))
return new MsmqTransportConfig
{
ErrorQueue = "error",
InputQueue = "PluginQueue",
MaxRetries = 1,
NumberOfWorkerThreads = 1
} as T;
return null;
}
}
}
public class SomeHandler : IHandleMessages<EventMessage1>
{
public void Handle(EventMessage1 message)
{
Debugger.Break();
}
}
}
And I don't get handler invoked.
If you have any ideas, please help. I'm fighting this problem a lot of time.
Also if full code need to be published, please tell.
I need several buses to solve the following problem :
I have my target application, and several plugins with it. We decided to make our plugins according to service bus pattern.
Each plugin can have several profiles.
So, target application(it is web app.) is publishing message, that something has changed in it. Each plugin which is subscribed to this message, need to do some action for each profile. But plugin knows nothing about its profiles (customers are writing plugins). Plugin should only have profile injected in it, when message handling started.
We decided to have some RecepientList (pattern is described in "Enterprise Integration Patterns"), which knows about plugin profiles, iterates through them and re-send messages with profiles injected.(So if plugin has several profiles, several messages will be sent to it).
But I don't want to have each plugin invoked in a new process. Perfectly I want to dynamically configure buses for each plugin during start. All in one process. But it seems I need to do it in separate AppDomains. So I have a problem described above:-).
Sergey,
I'm unclear as to why each plugin needs to have its own bus. Could they all not sit on the same bus? Each plugin developer would write their message handlers as before, and the subscriptions would happen automatically by the bus.
Then, also, you wouldn't need to specify to load each of the NServiceBus DLLs.
BTW, loading an assembly by name tends to cause problems - try using this to specify assemblies:
typeof(IMessage).Assembly, typeof(MsmqTransportConfig).Assembly, typeof(IConfigureThisEndpoint).Assembly

Threads in WCF service

there is a piece of code:
class WCFConsoleHostApp : IBank
{
private static int _instanceCounter;
public WCFConsoleHostApp ()
{
Interlocked.Increment(ref _instanceCounter);
Console.WriteLine(string.Format("{0:T} Instance nr " + _instanceCounter + " created", DateTime.Now));
}
private static int amount;
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(WCFConsoleHostApp));
host.Open();
Console.WriteLine("Host is running...");
Console.ReadLine();
}
#region IBank Members
BankOperationResult IBank.Put(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting...");
WCFConsoleHostApp.amount += amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
BankOperationResult IBank.Withdraw(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing...");
WCFConsoleHostApp.amount -= amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
#endregion
}
My test client application calls that service in 50 threads (service is PerCall). What I found very disturbing is when I added Thread.Sleep(20000) WCF creates one service instance per second using different thread from pool.
When I remove Thread.Sleep(20000) 50 instances are instanciated straight away, and about 2-4 threads are used to do it - which in fact I consider normal.
Could somebody explain why when Thread.Sleep causes those funny delays in creating instances?
You're mixing up your actual service implementation (the implementation of your IBank interface), and your service host in one and the same class.
This is definitely NOT good practice.
By default, WCF will by design instantiate a new separate copy of your service implementation class for each request coming in. This makes writing the service much easier (no need to fuss with multi-threading - each request gets its own class).
BUT: you shouldn't mix that with the ServiceHost, since you really only need one service host instance to host a service class that can handle hundreds or thousands of requests.
So - create one class
class BankImplementation : IBank
{
private static int _instanceCounter;
BankOperationResult IBank.Put(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting...");
//WCFConsoleHostApp.amount += amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
BankOperationResult IBank.Withdraw(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing...");
//WCFConsoleHostApp.amount -= amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
}
for your service code, and then a separate one (possibly even in a separate project all together) for hosting your service code:
class WCFConsoleHostApp
{
public WCFConsoleHostApp ()
{
Interlocked.Increment(ref _instanceCounter);
Console.WriteLine(string.Format("{0:T} Instance nr " + _instanceCounter + " created", DateTime.Now));
}
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(BankImplementation));
host.Open();
Console.WriteLine("Host is running...");
Console.ReadLine();
host.Close();
}
}
Now you get one instance of your WCFConsoleHostApp, which will spin up the WCF runtime at host.Open() and handle the requests by instantiating as many BankImplementation class instances as needed.
UPDATE: Well, a WCF service is also "throttled", e.g. you can tweak how many concurrent calls and instances there are. By default, you get 10 concurrent session and 16 concurrent calls. If your service is already handling 16 concurrent calls and those sleep for some time, no additional service instances will be creating and handled.
See this excellent blog post by Kenny Wolf on details about service throttling. You can tweak those maximums as you see fit.
I don't know that this is correct, but...
It might be that you're running into ThreadPool behavior rather than WCF behavior. Since the threads are staying open, the behavior of the ThreadPool may be that it will spin up additional threads to handle queued up work over time, as it will normally try to keep the thread count down to conserve resources.
So, theoretically, WCF will then queue a work item for each of the requests, but since the threads are not released for twenty seconds, they don't get serviced (past the initial request, that is). The ThreadPool sees this after a second, creates a new thread, and steals some work from the existing queue. Repeat every second.
You're pausing your service - or simulating long running jobs. wcf simply creates more threads to handle other clients that wants to be serviced.
I'm not 100% sure about this, but you may be running into throttling issues with your WCF service. Take a look at the Throttling section of this MSDN article. I hope this helps.