Introduction
In this article I will show how to run the Window version of Redis Server, or other executables, as a Windows service.
Here at CodeProject, we use Redis as a distributed cache. We store massive amounts of information such as Articles, Forum Messages and retrieve these items from the cache rather than the database.
Redis alllows us to store and retrieve full documents, rather than querying SQL for the various pieces and composing and formatting the document on each request. This is possible on our site because most of the information is read a lot more than it is written, and some information, such as number of views, can be a little stale.
While in production we run Redis on a Linux server, in out development environment we are running the Microsoft port of Redis on a Window 7 desktop. The problem is that Redis is not designed to be run as a Windows Service. This means that someone had to logon and run the Redis executable. This was fine when Chris was the only one logging into the server, but last week, I had to connect to install a copy of our Search Server for development purposes. Needless to say, this logged Chris off, and killed the Redis Server. I restarted it under my session.
Back on my machine, I am fixing a subtle caching bug, and when I start testing, the code is acting like there is a connection failure to the Redis Server. As the code I am changing is related to the detection of problems with the connection to Redis, I spin my wheels for half an hour or so before I realize that Chris has remoted into the server, killing my session and the Redis Server.
Both Chris and I tried a number of recommended methods for running Redis as a Windows Service, without any success. Having written several Windows Services to support various CodeProject processes, I decided to write a utility which would allow us to install and run an exe as a Windows Service. This utility is called Exe2Srvc
.
Using Exe2Srvc
Exe2Srv is a program that can be run as either a console application or installed as a Windows Service. This application reads the path to an executable, and the command line arguments from it .config file and then starts a the executable in a new Process.
The simplest way to use the executable is to
- copy the files in the binfiles download, or from bin/Release from the compiled source, into the directory containing the executable you wish to run as a Service.
- Edit the "Cmd" and "CmdArgs" values in the Exe2Srvc.exe.config to contain the full path to the executable, and the command line arguments required.
- Run the Install.bat file from a 'Run as Administrator' command shell.
- Use the Service Manager to:
- set the start mode to Automatic
- set the Recovery options. I usually set them to "Restart Service".
- start the Service.
For my tests, I downloaded Redis from Nuget, and copied the files from the /packages/Redis-64.2.6.12.1/tools
under the solution directory to C:/Redis
. I then copied the files from Exe2Srvc\bin\Release
to the same directory.
The Exe2Srvc.exe.config file contains:
="1.0"="utf-8"
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<appSettings>
<add key="Cmd" value="c:\Redis\redis-server.exe"/>
<add key="CmdArgs" value="c:\Redis\redis.conf --port 6379"/>
</appSettings>
</configuration>
If the path to the executable includes spaces, then the path needs to be in quotes. This can be accomplished by enclosing the double-quoted string in single-quotes as shown below.
="1.0"="utf-8"
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<appSettings>
<add key="Cmd" value='"c:Program Files\Redis\redis-server.exe"'/>
<add key="CmdArgs" value="c:\Redis\redis.conf --port 6379"/>
</appSettings>
</configuration>
The Install.bat
file, which must be run as an Administrator, uses the SC
command to install the Service.
sc create Redis binpath= "c:\Redis\Exe2Srvc.exe"
Then I use the Services Manager to configure the Service and start it.
Double Click on the Redis service to open the properties editor and select the Recovery tab. Set the options to restart the service on errors as shown.
Select the General tab.
- Set the Startup Type to Automatic,
- click the Start button
- and click the OK button.
Redis is now installed and running as a Windows Service. Use the redis-cli.exe
to test the connection.
How Exe2Srvc works
Creating a console application that can be run as a Windows Service used a technique shown to me by Steve Smith and based on an article Run Windows Service as a console program by Einar Egilsson.
Basically you create a console application, and change the Program
class to derive from ServiceBase
. In the Main
, the Environment.UserInteractive
property is used to determine whether the program is being run from the command line, or run as a Service.
The required command and commandline parameters are read from the LoadConfiguration
method.
private void LoadConfiguration
{
_cmd = ConfigurationManager.AppSettings["Cmd"];
_cmdArgs = ConfigurationManager.AppSettings["CmdArgs"];
if (string.IsNullOrWhiteSpace(_cmd))
throw new Exception("The appsetting 'Cmd' was not defined in the config file.");
}
This is called from the OnStart
method which is called from Main
when started as a console application, or by the Service
infrastructure when run as a Service. OnStart runs the executable in a new Process
as shown below.
protected override void OnStart(string[] args)
{
if (Environment.UserInteractive)
{
string message = String.Format"Starting {0} at {1}.", _serviceName, DateTime.Now);
Console.WriteLine(message);
}
LoadConfiguration();
ProcessStartInfo procInfo = new ProcessStartInfo(_cmd);
procInfo.UseShellExecute = false;
if (!string.IsNullOrWhiteSpace(_cmdArgs))
procInfo.Arguments = _cmdArgs;
_process = Process.Start(procInfo);
}
When the Service is stopped, the OnStop
method is called. This kills the Process
, waits for it to terminate, and disposes of the Process
. Make sure you wait for the Process
to terminate, failure to do so will result in improperly stopped Services that are difficult to remove and fix.
protected override void OnStop()
{
if (Environment.UserInteractive)
{
string message = String.Format("Stopping {0} at {1}.", _serviceName, DateTime.Now);
Console.WriteLine(message);
}
if (_process != null)
{
_process.Kill();
_process.WaitForExit();
_process.Dispose();
_process = null;
}
}
Points of Interest
I've attempted to make this as simple and flexible as possible, but it only meets my original requirement of being able to run the Windows version of Redis as a Windows Server. If you have additional requirement, feel free to modify the code to match your needs.
History
This is the original release.
31 Jan, 2014 - added example of specifying an executable path that contains spaces.