65.9K
CodeProject 正在变化。 阅读更多。
Home

将 Redis 作为 Windows 服务运行

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (22投票s)

2014 年 1 月 25 日

CPOL

5分钟阅读

viewsIcon

88546

downloadIcon

2753

一个用于将 Redis 或其他可执行文件作为 Windows 服务运行的实用程序。

引言

在本文中,我将演示如何将 Windows 版本的 Redis Server 或其他可执行文件作为 Windows 服务运行。

在 CodeProject,我们将 Redis 用作分布式缓存。我们存储大量信息,如文章、论坛消息,并从缓存中检索这些项目,而不是从数据库中检索。

Redis 允许我们存储和检索完整的文档,而不是在每次请求时查询 SQL 获取各个部分并进行组合和格式化。这在我们的网站上是可行的,因为大部分信息读取的次数远多于写入的次数,而且一些信息,例如查看次数,可能有点过时。

虽然在生产环境中,我们在 Linux 服务器上运行 Redis,但在我们的开发环境中,我们在 Windows 7 桌面机上运行 Microsoft 移植的 Redis。问题在于 Redis 的设计并非用于作为 Windows 服务运行。这意味着必须有人登录并运行 Redis 可执行文件。当 Chris 是唯一登录服务器的人时,这没问题,但上周,我需要连接来安装一个用于开发目的的搜索服务器副本。不用说,这导致 Chris 被注销,并且 Redis 服务器被终止。我在我的会话下重新启动了它。

回到我的机器上,我正在修复一个微妙的缓存错误,当我开始测试时,代码的行为就像 Redis 服务器连接失败一样。由于我正在更改的代码与检测 Redis 连接问题有关,我在花费大约半小时的时间后才意识到 Chris 已经远程登录到服务器,终止了我的会话和 Redis 服务器。

我和 Chris 都尝试了许多推荐的将 Redis 作为 Windows 服务运行的方法,但都没有成功。我曾编写过几个 Windows 服务来支持各种 CodeProject 进程,因此我决定编写一个实用程序,允许我们安装和运行一个 exe 作为 Windows 服务。这个实用程序名为 Exe2Srvc

使用 Exe2Srvc

Exe2Srv 是一个程序,可以作为控制台应用程序运行,也可以安装为 Windows 服务。此应用程序从其 .config 文件中读取可执行文件的路径和命令行参数,然后在新进程中启动该可执行文件。

使用该可执行文件的最简单方法是

  1. binfiles 下载中的文件,或从编译源代码的 bin/Release 目录中的文件,复制到要作为服务运行的可执行文件所在的目录。
  2. 编辑 Exe2Srvc.exe.config 中的 "Cmd" 和 "CmdArgs" 值,以包含可执行文件的完整路径和所需的命令行参数。
  3. 从“以管理员身份运行”命令提示符运行 Install.bat 文件。
  4. 使用服务管理器
     
    • 将启动模式设置为“自动”
    • 设置恢复选项。我通常将它们设置为“重新启动服务”。
    • 启动服务。

对于我的测试,我从 Nuget 下载了 Redis,并将解决方案目录下的 /packages/Redis-64.2.6.12.1/tools 中的文件复制到 C:/Redis。然后,我将 Exe2Srvc\bin\Release 中的文件复制到同一目录。

Exe2Srvc.exe.config 文件包含

<?xml version="1.0" encoding="utf-8" />
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
    </startup>
    <appSettings>
        <!-- set Cmd to the executable file name. -->
        <!-- set CmdArg to any command arguments required. -->
        <add key="Cmd"      value="c:\Redis\redis-server.exe"/>
        <add key="CmdArgs"  value="c:\Redis\redis.conf --port 6379"/> 
    </appSettings>
</configuration>

如果可执行文件的路径包含空格,则路径需要用引号括起来。可以通过像下面那样用单引号括起来的双引号字符串来完成此操作。

<?xml version="1.0" encoding="utf-8" />
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
    </startup>
    <appSettings>
        <!-- set Cmd to the executable file name. -->
        <!-- set CmdArg to any command arguments required. -->
        <add key="Cmd"      value='"c:Program Files\Redis\redis-server.exe"'/>
        <add key="CmdArgs"  value="c:\Redis\redis.conf --port 6379"/> 
    </appSettings>
</configuration>

Install.bat 文件必须以管理员身份运行,它使用 SC 命令来安装服务。

sc create Redis binpath= "c:\Redis\Exe2Srvc.exe"

然后,我使用服务管理器配置服务并启动它。

Service Manager

双击 Redis 服务以打开属性编辑器,然后选择“恢复”选项卡。将选项设置为在出现错误时重新启动服务,如图所示。

 Service Manager

选择“常规”选项卡。 

  • 将“启动类型”设置为“自动”,
  • 点击“启动”按钮
  • 然后点击“确定”按钮。
Service Manager

Redis 现在已安装并作为 Windows 服务运行。使用 redis-cli.exe 测试连接。

Exe2Srvc 的工作原理 

创建可作为 Windows 服务运行的控制台应用程序,使用了 Steve Smith 教给我的技术,该技术基于 Einar Egilsson 的文章 “将 Windows 服务作为控制台程序运行”

基本上,您创建一个控制台应用程序,并将 Program 类更改为继承自 ServiceBase。在 Main 中,使用 Environment.UserInteractive 属性来确定程序是直接从命令行运行,还是作为服务运行。

所需的命令和命令行参数从 LoadConfiguration 方法读取。

/// <summary>
/// Loads the configuration parameters from the application config file
/// </summary>
private void LoadConfiguration
{
    // Load the executable filename and command arguements from config file.
    _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.");
}

这从 OnStart 方法调用,OnStart 方法在作为控制台应用程序启动时由 Main 调用,或者在作为服务运行时由 Service 基础结构调用。OnStart 在新 Process 中运行可执行文件,如下所示。

/// <summary>
/// When implemented in a derived class, executes when a Start command is sent to the
/// service by the Service Control Manager (SCM) or when the operating system starts
/// (for a service that starts automatically).
/// Specifies actions to take when the service starts.
/// </summary>
/// <param name="args";>
/// Data passed by the start command.
/// </param>
protected override void OnStart(string[] args)
{
    if (Environment.UserInteractive)
    {
        string message = String.Format"Starting {0} at {1}.", _serviceName, DateTime.Now);
         Console.WriteLine(message);
    }
 
    // loading the configuration file info here allows the service to be stopped,
    // the configuration modified, and the service restarted.
    LoadConfiguration();
 
     // Start the executable.
      ProcessStartInfo procInfo = new ProcessStartInfo(_cmd);
      procInfo.UseShellExecute = false;
 
       if (!string.IsNullOrWhiteSpace(_cmdArgs))
           procInfo.Arguments = _cmdArgs;
 
       _process = Process.Start(procInfo);
 }
 

当服务停止时,会调用 OnStop 方法。这会终止 Process ,等待其终止,然后释放 Process 。请确保等待 Process 终止,否则将导致服务停止不当,难以删除和修复。

/// <summary>
/// When implemented in a derived class, executes when a Stop command is sent to the service
/// by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
/// </summary>
/// <remarks>Stops the background tasks.</remarks>
protected override void OnStop()
{
    if (Environment.UserInteractive)
    {
        string message = String.Format("Stopping {0} at {1}.", _serviceName, DateTime.Now);
         Console.WriteLine(message);
     }
           
     // Kill the process
     if (_process != null)
     {
         _process.Kill();
         _process.WaitForExit();
         _process.Dispose();
         _process = null;
      }
}

关注点

我已尽力使其尽可能简单和灵活,但它仅满足我运行 Windows 版 Redis 作为 Windows Server 的原始要求。如果您有其他需求,请随时修改代码以满足您的需求。

历史

这是原始版本。

2014 年 1 月 31 日 - 添加了指定包含空格的可执行文件路径的示例。 

 

© . All rights reserved.