Introduction
The Singleton pattern is often considered harmful in a distributed application, especially for it's scalability. At the same time it is an easy way to keep global application state. So how do we implement it? This article will not discuss the double-checked pattern neither the benefits of lazy instantiated singletons or how much a singleton can damage a system's scalability; instead it addresses the performance issues related to a non-clustered singleton.
A remoting based application server can essentially use two kinds of singleton: the classic one (a static instance in a normal class), or the remoting based singleton. The problem with the first solution is that two instances of the same app server can not share the same singleton, while the second option introduces the remoting overhead in the common case where only one instance of the app server runs.
A combination of the two options gives a third option: make a normal singleton (the former case) accessible by direct reference inside the same app server instance and by a remoting proxy from the other app server instances. In this way, the common case of only a single instance being running, does not deal with remoting issues, while the need to synchronize data between different instances (in different app domains) disappears. If there are N app server instances, only N-1 of them access the singleton through remoting. Obviously, this optimization can be significant only when N is very small.
The point here is to write and compile the app server code only once, and run it in different configurations.
Background
Basic remoting and the Singleton design pattern knowledge are needed to understand this article.
Using the code
The singleton class (Singleton
) must inherit from MarshalByRefObject
, in order to be exposed via remoting. It runs in an app domain, while the other only see the interface it implements (ISingleton
).
public class Singleton : MarshalByRefObject, ISingleton
{
...
}
The factory is a client activated marshal-by-ref class, and has a static member called instance
. To instantiate the Singleton
class it uses reflection, in order to eliminate the compile-time dependency on the singleton implementation. The reflection performance penalty only occurs once; that's why I preferred reflection instead of using another factory for instantiating the singleton factory. Anyway, in the sample code the dependency removal does not really help. It becomes useful when there are other (UI) projects that need access to the singleton. If you add a reference from the Common project to the SingletonImpl project, and replace the following lines:
ObjectHandle objectHandle =
Activator.CreateInstance("SingletonImpl", "Singleton");
instance = (ISingleton)objectHandle.Unwrap();
with
instance = new Singleton();
the sample still works fine (after removing the circular dependency).
In order to test the sample code provided, first run the solution. This will actually run the (main) app domain that will contain the singleton instance. Secondly, copy Server project output to another location(s), except the SingletonImpl.* files and replace Server.exe.config with appWithoutSingleton.config file. Then run all the Server.exe applications in those locations.
The sample application simply gets and sets an integer member of the singleton, and tests if they are the same. If not, it prints a message. It's easy to notice that in the main app domain, the value got is almost all the time equal to the value just set, while the other clients of the singleton suffer from the remoting penalty (between get and set, the integer member of the singleton is very often changed, especially by the code running in the main app domain).
History