控制台程序秒變Windows服務(Topshelf)


項目中有些時候需要寫服務,一般我們都是先創建控制台程序,測試,運行,成功之后再創建windows服務程序,這樣好麻煩啊,有沒有簡單的控制台程序直接變成Widnows服務,經過查找,找到了Topshelf。Topshelf是一個托管使用.NET框架編寫的服務的框架,簡化了服務的創建,允許開發人員創建一個簡單的控制台應用程序,可以使用Topshelf作為服務安裝。

使用NSSM工具將exe封裝為服務

根據下面的評論,增加了這一部分的內容,NSSM是一款比較好用的將exe封裝為服務的工具

1.下載NSSM官網https://nssm.cc/,download
2. 解壓
3. 在cmd中找到nssm.exe
例如:E:\nssm-2.24\nssm-2.24\win64>

4. 安裝服務
安裝服務:nssm install <servicename>

寫的控制台程序需要注意,不要一閃而過,也就是運行控制台程序的時候,控制台不會自己關閉,自己關閉意味着程序結束,服務啟動之后也會結束,所以最后的 Console.ReadKey();還是需要的。

啟動服務:nssm start <servicename>
停止服務:nssm stop <servicename>
重啟服務: nssm restart <servicename>
服務刪除:nssm remove <servicename>

這個工具的使用是根據下面的評論來的,比較好用,但是也有點小缺點,就是程序都需要寫在main中,需要控制好運行的順序,如果要服務開始的時候運行什么,中間運行什么,停止的時候運行什么,這個程序就沒辦法控制了,如果是一般的服務用工具就可以了,如果比較復雜,需要做流程控制,可以參考下面的程序。

NSSM .NET Core服務安裝

Path:dotnet所在的目錄,一般默認是在C:\Program Files\dotnet\dotnet.exe;

Startup directory:程序所在的目錄,就是最后程序dll所在的目錄;

Arguments:程序dll的名稱,一般是項目名加上.dll;

Service name:在此寫上服務的名稱即可。

然后點擊Install service按鈕就完成了部署。

最后到Windows服務管理界面找到安裝的服務名稱,右鍵啟動即可,或者直接運行nssm start 服務名稱,也可以運行net start 服務名稱。這樣就可以非常簡單的把.Net Core托管至Window服務中。方便管理、啟動、停止等等。

Topshelf介紹

Topshelf是一個托管使用.NET框架編寫的服務的框架。簡化了服務的創建,允許開發人員創建一個簡單的控制台應用程序,可以使用Topshelf作為服務安裝。原因很簡單:調試控制台應用程序比使用服務要容易得多。一旦應用程序經過測試並可以投入生產,Topshelf便可以輕松地將應用程序作為服務進行安裝。這是一個開源的項目,項目地址,Nuget上可以搜到響應的庫。

Topshelf使用

1.創建控制台程序
2.安裝Topshelf,在Nuget上搜下
3.安裝NLog、NLog.Config,目的是為了看日志,可以清楚的知道服務在運行,可以不要
NLog.Config簡單配置

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">

  <!-- optional, add some variables
  https://github.com/nlog/NLog/wiki/Configuration-file#variables
  -->
  <variable name="myvar" value="myvalue"/>

  <!--
  See https://github.com/nlog/nlog/wiki/Configuration-file
  for information on customizing logging rules and outputs.
   -->
  <targets>

    <!--
    add your targets here
    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
    -->

    <!--
    Write events to a file with the date in the filename.
    -->
    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
        layout="${longdate} ${uppercase:${level}} ${message}" />
  </targets>

  <rules>
    <!-- add your logging rules here -->

    <!--
    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
    -->
    <logger name="*" minlevel="Debug" writeTo="f" />
  </rules>
</nlog>

4.代碼實現

class Program
{
    private static readonly Logger logger = LogManager.GetLogger("Program");
    static void Main(string[] args)
    {
        logger.Info($"Main主程序{DateTime.Now}");
        var rc = HostFactory.Run(x =>                         //1.啟動程序
        {
            logger.Info($"主程序{DateTime.Now}");
            x.Service<TownCrier>(s =>                         //2.設置服務類型
            {
                s.ConstructUsing(name => new TownCrier());    //3.創建服務實例
                s.WhenStarted(tc => tc.Start());              //4.啟動程序
                s.WhenStopped(tc => tc.Stop());               //5.停止程序
            });
            x.RunAsLocalSystem();                             //6.本地系統運行

            x.SetDescription("超級簡單的windows服務");         //7.windows服務的描述
            x.SetDisplayName("SimpleWindowsService 服務");                        //8.windows服務的顯示名稱
            x.SetServiceName("SimpleWindowsService");                        //9.windows服務的服務名稱
        });

        var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());  //11.退出程序
        Environment.ExitCode = exitCode;

    }
}

public class TownCrier
{
    private static readonly Logger logger = LogManager.GetLogger("logTest");
    readonly Timer _timer; //System.Timers不要引用錯誤
    public TownCrier()
    {
        _timer = new Timer(1000) { AutoReset = true };
        _timer.Elapsed += (sender, eventArgs) =>
        {
            Console.WriteLine($"It is {DateTime.Now} and all is well");
            logger.Info($"It is {DateTime.Now} and all is well");
        };
    }
    public void Start() { _timer.Start(); }
    public void Stop() { _timer.Stop(); }
}

必須有啟動方法(Start)和停止方法(Stop)

4. 運行控制台程序,也可以是調試,效果如下

5. 程序調試運行成功,一切ok,現在可以安裝服務了,以管理員身份運行cmd找到對應路徑,開始安裝

安裝:SimpleWindowsService.exe install

查看服務

啟動:SimpleWindowsService.exe start

服務已經運行,查看運行情況,日志

卸載:SimpleWindowsService.exe uninstall

可以看到服務已經沒有了

停止:SimpleWindowsService.exe stop
停止之后可以再次啟動,這個功能不介紹了,卸載服務的時候會調用這個方法。

Topshelf的其他功能

安裝動作之前:Topshelf允許指定在安裝服務之前執行的操作。請注意,只有在安裝服務時才會執行此操作。

HostFactory.New(x =>
{
    x.BeforeInstall(settings => { ... });
});

安裝動作后:Topshelf允許指定在安裝服務后執行的操作。請注意,只有在安裝服務時才會執行此操作。

HostFactory.New(x =>
{
    x.AfterInstall(settings => { ... });
});

在卸載操作之前:Topshelf允許指定在卸載服務之前執行的操作。請注意,只有在卸載服務時才會執行此操作。

HostFactory.New(x =>
{
    x.BeforeUninstall(() => { ... });
});

卸載操作后:Topshelf允許指定在卸載服務后執行的操作。請注意,只有在卸載服務時才會執行此操作。

HostFactory.New(x =>
{
    x.AfterUninstall(() => { ... });
});

異常:為服務運行時拋出的異常提供回調。此回調不是處理程序,不會影響Topshelf已提供的默認異常處理。它旨在提供對觸發外部操作,日志記錄等的拋出異常的可見性。

HostFactory.New(x =>
{
    x.OnException(ex =>
    {
        // Do something with the exception
    });
});

其他的一些功能,如果需要可以查看英文官網文檔

定時任務的服務

一般的服務都沒有這么簡單,一般都需要定時任務,這里的定時任務服務用到了FluentScheduler,FluentScheduler定時器介紹 ,這篇文章對FluentScheduler定時器進行了詳細的介紹,這里不再介紹,只展示使用。

/// <summary>
/// 用MySchedule的任務定時功能
/// </summary>
public class MyJob {
    private static readonly Logger logger = LogManager.GetLogger("MyJob");
    public MyJob() { }
    public void Start()
    {
        logger.Info($"MySchedule啟動 {DateTime.Now}");
        JobManager.Initialize(new MySchedule());
    }
    public void Stop()
    {
        logger.Info($"MySchedule停止 {DateTime.Now}");
        JobManager.Stop();
    }
}

/// <summary>
/// 定時器
/// </summary>
public class MySchedule : Registry
{
    private static readonly Logger logger = LogManager.GetLogger("MySchedule");
    public MySchedule()
    {
        SetNewsSchedule();
    }

    /// <summary>
    /// 設置任務
    /// </summary>
    private void SetNewsSchedule()
    {
        //獲取鏈接發送郵件
        Schedule(() =>
        {
            logger.Info($"MySchedule運行 {DateTime.Now}");
        }
        ).ToRunNow().AndEvery(1000).Milliseconds();
    }
}

控制台程序調用

class Program
{
    private static readonly Logger logger = LogManager.GetLogger("Program");
    static void Main(string[] args)
    {
        logger.Info($"Main主程序{DateTime.Now}");
        var rc = HostFactory.Run(x =>                         //1.啟動程序
        {
            logger.Info($"主程序{DateTime.Now}");
            x.Service<MyJob>(s =>                         //2.設置服務類型
            {
                s.ConstructUsing(name => new MyJob());    //3.創建服務實例
                s.WhenStarted(tc => tc.Start());              //4.啟動程序
                s.WhenStopped(tc => tc.Stop());               //5.停止程序
            });
            x.RunAsLocalSystem();                             //6.本地系統運行

            x.SetDescription("超級簡單的windows服務");         //7.windows服務的描述
            x.SetDisplayName("SimpleWindowsService 服務");                        //8.windows服務的顯示名稱
            x.SetServiceName("SimpleWindowsService");                        //9.windows服務的服務名稱
        });
        var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());  //11.退出程序
        Environment.ExitCode = exitCode;

    }
}

服務安裝兩步走,管理員cmd,SimpleWindowsService.exe install, SimpleWindowsService.exe start

總結

1. 寫控制台程序

2.管理員cmd,SimpleWindowsService.exe install

3.啟動服務SimpleWindowsService.exe start

4.卸載服務SimpleWindowsService.exe uninstall


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM