A: background

1. Tell a story

There was a project delivery last week, because it was a hospital-level project that needed to be independently deployed on the customer’s LAN. Program: Netcore 2.0, operating system: You need to install Visual C++ Redistributable for Visual Studio 2015. Happy download down is installation failure, again looking for information said to hit a pile of system patches, made a day!! 😤 😤 😤

Finally, the environment is installed, because it is the Console service program, and we need to make it into Windows Service. See the company’s previous deployment mode is using VS Windows Service template.

There are three other interesting ways to deploy services in this article. Let’s compare them together.

2. Test code

To be more formalized, I listen for Ctrl + C events in the Console as follows:


    public class Program
    {
        public static void Main(string[] args)
        {
            var dir = AppDomain.CurrentDomain.BaseDirectory;


            var cts = new CancellationTokenSource();

            var bgtask = Task.Factory.StartNew(() => { TestService.Run(cts.Token); });

            Console.CancelKeyPress += (s, e) =>
            {
                TestService.Log($"{DateTime.Now}Background test service, ready for resource cleanup!");

                cts.Cancel();
                bgtask.Wait();

                TestService.Log($"{DateTime.Now}Congratulations, the Test service program has successfully exited!);
            };

            TestService.Log($"{DateTime.Now}The backend server is running properly!"); bgtask.Wait(); }}Copy the code

With this template, define a TestService to perform background tasks continuously as follows:


    public class TestService
    {
        public static void Run(CancellationToken token)
        {
            while(! token.IsCancellationRequested) { Console.WriteLine($"{DateTime.Now}Mysql > select * from mysql;);
                System.Threading.Thread.Sleep(2000);
                Console.WriteLine($"{DateTime.Now}: 2. Get redis");
                System.Threading.Thread.Sleep(2000);
                Console.WriteLine($"{DateTime.Now}: 3. Update monogdb");
                System.Threading.Thread.Sleep(2000);
                Console.WriteLine($"{DateTime.Now}: 4. Notify Kafka.");
                System.Threading.Thread.Sleep(2000);
                Console.WriteLine($"{DateTime.Now}: 5. All business has been processed.");
                System.Threading.Thread.Sleep(2000); }}public static void Log(string msg)
        {
            Console.WriteLine(msg);
            File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "//1.log".$"{msg}\r\n"); }}Copy the code

Two: Four service deployment modes

1. Traditional Windows Service templates

Believe done windowsservice deployment friend knows this way, the need in the vs new template, and then define a subclass MySerivce inherit from ServiceBase, rewrite the parent class OnStart and OnStop method, the code is as follows:


    partial class MyService : ServiceBase
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        Task bgtask;

        public MyService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // TODO: Add code here to start your service.
            bgtask = Task.Factory.StartNew(() => { TestService.Run(cts.Token); });
        }

        protected override void OnStop()
        {
            // TODO: Add code here to perform any tear-down necessary to stop your service.cts.Cancel(); bgtask.Wait(); }}Copy the code

Refactor the Main method:


    public class Program
    {
        public static void Main(string[] args)
        {
            ServiceBase.Run(newMyService()); }}Copy the code

Finally, publish and install the Windows SC service.

Sc create MyService BinPath=E:\net5\ConsoleApp1\ConsoleApp2\bin\Release\ netcoreApp3.1 \publish\ consoleapp2. exe sc start MyServiceCopy the code

To verify that the program is working properly, go to the services panel and the installation path to view the startup log.

Here are the pros and cons:

  • Disadvantages: need to modify the code, and once the code is changed, you can not double-click exe to execute, resulting in no debugging.
  • Advantages: No additional dependence, all using built-in technology.

2. Use open source Topshelf

If you are interested, you can take a look at its official website: Topshelf-project.com is more convenient and concise. I use Nuget install-package Topshelf to access the project. According to the official demo, I need to implement the Start and Stop methods in TestService.


public class TestService
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token;
        Task bgtask;

        public TestService()
        {
            token = cts.Token;
        }

        public void Start()
        {
            bgtask = Task.Run(() =>
            {
                while(! token.IsCancellationRequested) { Log($"{DateTime.Now}Mysql > select * from mysql;);
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 2. Get redis");
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 3. Update monogdb");
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 4. Notify Kafka.");
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 5. All business has been processed.");
                    System.Threading.Thread.Sleep(2000); }}); }public void Stop()
        {
            cts.Cancel();
            bgtask.Wait();
        }

        public static void Log(string msg)
        {
            Console.WriteLine(msg);
            File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "1.log".$"{msg}\r\n"); }}Copy the code

Next, modify the Main method to use its HostFactory class as follows:


        public static void Main(string[] args)
        {
            var rc = HostFactory.Run(x =>                                   / / 1
            {
                x.Service<TestService>(s =>                                   / / 2
                {
                    s.ConstructUsing(name => new TestService());            / / 3
                    s.WhenStarted(tc => tc.Start());                         / / 4
                    s.WhenStopped(tc => tc.Stop());                          / / 5
                });
                x.RunAsLocalSystem();                                       / / 6
                x.StartAutomatically();

                x.SetDescription("TestService2 Topshelf Host");                   / / 7
                x.SetDisplayName("MyService2");                                  / / 8
                x.SetServiceName("MyService2");                                  / / 9
            });                                                             / / 10

            var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());  / / 11

            Environment.ExitCode = exitCode;
        }

Copy the code

As can be seen from the above code, the main thing is to do some service information configuration, and then you can publish the project and use XXx. exe install to install the service, as shown in the following figure:

E: \ net5 \ ConsoleApp1 \ ConsoleApp5 \ bin \ Release \ netcoreapp3.1 \ called publish2 > ConsoleApp5. Exe install Configuration Result: [Success] Name MyService2 [Success] Description TestService2 Topshelf Host [Success] ServiceName MyService2 Topshelf V4.2.1.215, .NET Framework V3.1.9 Running a transacted installation. Beginning the Install phase of the installation. Installing MyService2 service Installing service MyService2... Service MyService2 has been successfully installed. The Install phase completed successfully, and the Commit phase is beginning. The Commit phase completed successfully. The transacted install has completed.Copy the code

According to the output information, it has been successfully installed. How do you feel about the advantages and disadvantages of this method?

  • Cons: Third-party toolkits to install, code to modify, and pretty big…
  • Advantages: Debugging can also be achieved by double-clicking, and some built-in listening of the system is realized, such as Ctrl + C

3. Use Microsoft’s new built-in Hosting

In Netcore, Console, MVC and WebApi are all Console modes. For example, I created a new WebApi.

If you want to add a Hosting service to Windows Server, you can add a Hosting service to Windows Server. You can add a Hosting service to Windows Server.


nuget Install-Package Microsoft.Extensions.Hosting
nuget Install-Package Microsoft.Extensions.Hosting.WindowsServices

Copy the code

Thankfully, the package’s minimum dependency is.netStandard 2.0, which means that both the.NET Framework 4.6.1 + and.netcore 2.0 + work, 🐂👃

TestService overrides the ExecuteAsync method in BackgroundService.


    public class TestService : BackgroundService
    {
        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.Run(() =>
            {
                while(! stoppingToken.IsCancellationRequested) { Log($"{DateTime.Now}Mysql > select * from mysql;);
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 2. Get redis");
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 3. Update monogdb");
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 4. Notify Kafka.");
                    System.Threading.Thread.Sleep(2000);
                    Log($"{DateTime.Now}: 5. All business has been processed.");
                    System.Threading.Thread.Sleep(2000); }}); }public static void Log(string msg)
        {
            Console.WriteLine(msg);
            File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "1.log".$"{msg}\r\n"); }}Copy the code

Then I’ll modify the Main method.


    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<TestService>();
            });
    }

Copy the code

A: wow! The familiar code pops up in front of you, and double-click Console to make it even more familiar

Finally, the sc command can be used to make the service.

  • Disadvantages: Less intrusive code, slightly more dependencies introduced
  • Pros: Microsoft Decent Pedigree, powerful, built – in logging support

4. NSSM third-party tools

The first three are either built-in templates, or the installation of DLLS, is there a way to really zero code intrusion? You don’t need to change any code, just release the code and install it with the following command:


C:\Windows\system32>cdC: \ xcode \ soft \ \ win64 NSSM 2.24 C: \ xcode \ soft \ \ win64 NSSM 2.24 > NSSM install TestService3 E:\net5\ConsoleApp1\ConsoleApp6\bin\Release\ netcoreApp3.1 \publish\ consoleapp6. exe && NSSM start TestService3 Service"TestService3"installed successfully! TestService3: START: The operation succeeds.Copy the code

See, I didn’t really touch any code, and the service was installed.

  • Disadvantages: Third-party tools need to be installed
  • Advantages: Zero intrusion into code

Three:

If I have a choice, I prefer the combination of 3+4. At the code level, I prefer to use Microsoft’s new Hosting Hosting. In service deployment, I prefer NSSM, after all, it is much more flexible and powerful than SC. Welcome to add message! 😁 😁 😁

For more high-quality dry goods: See my GitHub:dotnetfly