CLR Hosting - A flexible, managed plugin system, part 2
Last time I explained how the managed side of my flexible hosting server architecture was structured. I dealt with the IDL required to generate the COM interfaces which were used to communicate between unmanaged code and managed code and detailed how the custom
AppDomainManager object allowed me to load and execute managed code in such a way that it was easy to update the managed applications whilst the server was running.
This time I’ll cover the unmanaged side of things. This is actually where much of the complexity lives. The code that actually manages the creation and destruction of the application domains in which the managed applications run and which deals with wiring them up to the network endpoints that are accepting connections for them.
The unmanaged code has an object which represents the collection of applications that it runs. This
CApplicationCollection class contains the CLR hosting code,
CManagedHost, a collection of application details for each application that it is configured to run and an additional collection of running applications. Thus the application details collection represents all of the applications than CAN run and the application collection represents all of the applications that ARE running.
This allows me to load a set of applications that can be run when the server starts up and start them. I can then tell each network endpoint the name of the application that connections are to be routed to and as each new connection comes in the socket server that is managing that end point can look up the application by name in the
CApplicationCollection and connect the connection to it. If the application is not running then the connection will fail and be closed after issuing an error message.
Since there’s a difference between applications that the server CAN run and applications that ARE running I can easily restart an application by locating the running instance, telling it we’re about to shut down and then unloading its application domain. Meanwhile I can create a new application domain, start a new copy of the managed application, based on the application details that are stored for the application, and insert it into the running applications collection. In fact I do this in reverse, starting and inserting the new application and then shutting down the old. This minimises the time when an application isn’t available.
Whenever an application is stopped I need to be able to walk the list of connections that are currently connected to the application and terminate them. This requires that the unmanaged application objects maintain a collection of active connections. This deals with the ‘kill’ style restarts that I spoke of in the previous article. To handle the ‘kind’ restarts I need to be able to keep a running application around after it has been removed from the running applications collection and only unload its application domain when all currently active connections have been completed. To enable this the application object is reference counted and the application domain is unloaded when the application object is deleted. This allows me to have each active connection hold a reference to the application that it is connected to and also have the
CApplicationCollection hold a reference whilst it has the application in its collection of running applications. When the application is restarted it is removed from the running applications collection and the reference owned by the
CApplicationCollection is released, now only the active connections are keeping the application alive and when the last of those ends the application domain is unloaded. Meanwhile we’ve already started a new copy of the application and placed it in the running applications collection and so new connections go to the new copy of the application and existing connections continue in the old copy.
Automatic restarts are supported by enabling shadow copying on the application domains so that the assemblies aren’t locked whilst the application is running and adding a directory change monitor to look for updated files. The directory change monitor starts a 10 second timer when it spots a file change and when the timer expires the application is restarted. The server demonstrates both kind and kill automatic restarts. The timer is necessary to prevent multiple restarts when files are updated by hand, ideally you set a timer for long enough to allow someone to copy or edit files and when the “quiet period” has expired the application is restarted.
Finally a management socket server presents a simple text based protocol that’s accessible via telnet which allows the user to list and manipulate the applications. Right now you can’t add new applications and endpoints to a running server but there’s nothing in the architecture to prevent it. Likewise you can’t remove endpoints but it could be added.
This code is all included in a new example server for The Server Framework which will ship with version 6.3.