Deploy the console as a Windows service. One of the four ways is always suitable for you!

Time:2021-12-5

1: Background

1. Tell a story

A project was delivered last week because it is a hospital level project and needs to be deployed independently on the customer’s LAN. Procedure:netcore 2.0, operating system:windows server 2012, the matter of Keng father came. NETCORE SDK couldn’t be installed all the time. I found information on the Internet and said that it needs to be installed firstVisual C++ Redistributable for Visual Studio 2015, happy to download it, but the installation failed again. I looked for information again and said I had to make a pilewindows xp , all day!!!

The environment is finally installed. Because it is a console service program, it has to be made into a Windows service. According to the previous deployment method of the company, the vs windows service template is adopted, as shown in the following figure:

How to say, this method is too old. In this article, we will talk about three other interesting service deployment methods in addition to this method. Just compare them together!

2. Test code

In order to be more formal, I listen for Ctrl + C events in the console. The code is 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, test service program has exited normally!");
            };

            Testservice. Log ($"{datetime. Now} back end service program starts normally!");

            bgtask.Wait();
        }
    }

With this template, a testservice is defined to continuously execute background tasks. The code is as follows:

public class TestService
    {
        public static void Run(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                Console. Writeline ($"{datetime. Now}: 1. Get 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. Notification Kafka");
                System.Threading.Thread.Sleep(2000);
                Console. Writeline ($"{datetime. Now}: 5. All business processing is completed");
                System.Threading.Thread.Sleep(2000);
            }
        }

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

2: Four service deployment modes

1. Traditional windows service template

I believe all friends who have deployed windows service know this method. You need to create a new template in VS, define a subclass myservce, inherit from servicebase, and override the OnStart and onstop methods of the parent class. 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();
        }
    }

Refactor the main method:

public class Program
    {
        public static void Main(string[] args)
        {
            ServiceBase.Run(new MyService());
        }
    }

Finally, execute publish and install the service with the SC provided by windows.

sc create MyService BinPath=E:\net5\ConsoleApp1\ConsoleApp2\bin\Release\netcoreapp3.1\publish\ConsoleApp2.exe
sc start MyService

To verify whether the program is running normally, you can go to the service panel and the installation path to view the startup log.

Let’s talk about the advantages and disadvantages:

  • Disadvantages: you need to modify the code, and once the code is changed, you can’t double-click exe to execute, resulting in inability to debug.
  • Advantages: no additional dependence is required, and all built-in technologies are adopted.

2. Use the open source topshelf

If you are interested, you can take a look at its official website:http://topshelf-project.comIt is light and simple to usenuget Install-Package TopshelfAccess the project. According to the official demo, I need to implement the start and stop methods in testservice. The modifications are as follows:

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}: 1. Get 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. Notification Kafka");
                    System.Threading.Thread.Sleep(2000);
                    Log ($"{datetime. Now}: 5. All business processing is completed");
                    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");
        }
    }

Next, modify the main method and use its hostfactory class. The code is as follows:

public static void Main(string[] args)
        {
            var rc = HostFactory.Run(x =>                                   //1
            {
                x.Service(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;
        }

As can be seen from the above code, it is mainly to configure the information of some services, then publish the project, and use xxx.exe install to install the services, as shown in the following figure:

E:\net5\ConsoleApp1\ConsoleApp5\bin\Release\netcoreapp3.1\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.

From the output information, it has been installed successfully. What do you think of the advantages and disadvantages of this method?

  • Disadvantages: you need to install a third-party toolkit, modify the code, and it’s quite large…
  • Advantages: double click can also be debugged, which realizes some built-in monitoring of the system, such as Ctrl + C

3. Use Microsoft’s new built-in hosting

Speaking of this, I believe you will not be unfamiliar. In NETCORE, console, MVC and webapi are all in console mode. For example, I have created the following webapi.

Here I have an idea. Can I deduct hosting in main for my service? That’s really true. Don’t say, it’s true. Just install hosting + for Windows service.

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

Fortunately, the minimum dependency of the package is.NETStandard 2.0, which means that both. Net framework 4.6.1 + and. NETCORE 2.0 + can be used,

The next step is to transform the executeasync method in the parent backgroundservice of testservice to override, as shown in the following code:

public class TestService : BackgroundService
    {
        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.Run(() =>
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    Log ($"{datetime. Now}: 1. Get 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. Notification Kafka");
                    System.Threading.Thread.Sleep(2000);
                    Log ($"{datetime. Now}: 5. All business processing is completed");
                    System.Threading.Thread.Sleep(2000);
                }
            });
        }

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

Then transform 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();
            });
    }

WOW! Is the familiar code in front of you? Is it more familiar to double-click console~~~

Finally, you can use the SC command to make a service.

  • Disadvantages: a small amount of code is intrusive and introduces a little more dependencies
  • Advantages: Microsoft decent blood, powerful function, built-in log support

4. NSSM third party tools

The first three methods are either built-in templates or DLL installation. Is there one that can really control the codeZero invasionAnd? There are many wonders in the world. You can take a look at this tool:http://www.nssm.cc, you do not need to modify any code. After publishing the code, you can install it with the following command:

C:\Windows\system32>cd C:\xcode\soft\nssm-2.24\win64

C:\xcode\soft\nssm-2.24\win64>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 completed successfully.

See, I really didn’t move any code, and the service installation was completed.

  • Disadvantages: third party tools need to be installed
  • Advantages: zero code intrusion

3: Summary

If you let me choose, I like the 3 + 4 combination. At the code level, I prefer to use Microsoft’s new hosting bearer. I prefer NSSM for service deployment. After all, it is much more flexible and powerful than sc. I don’t know which deployment method you prefer? Welcome to leave a message!

More high quality dry goods: see my GitHub:dotnetfly

图片名称