本文转载自微信公众号《老王加》,老王加作者老王。转载本文请联系老王Plus公众号。前言说到服务器应用,最常见的就是API服务。此外,还有一类应用程序,如Socket服务器。这类应用本身没有web层,当然也不属于API服务。你平常都做什么?随意的做法是创建一个Console应用,加载到后台一直运行。其实还有一种做法,就是把应用程序加载到Services中,让应用程序响应一个Service。这样就可以依靠操作系统的Servicesmanager进行统一管理、自动运行和故障处理。dotnet做WindowService的内容,网上有很多。今天写了一个Linux下做Service的方法。在Linux下创建一个Service应用在LInux下创建一个Service应用其实很简单,只需按以下步骤操作:1.创建一个带有Worker模板的工程。如果你习惯在VS上创建项目,你可以找到一个WorkerService模板。我习惯从命令行创建,只需一个命令:%dotnetnewworker-oprojectnameDotnet会自动创建一个项目并自动引用Microsoft.Extensions.Hosting包,因为这本身就是一个Self-Hosting应用程序。2、增加Linux服务扩展包。其实这是一个包:Microsoft.Extensions.Hosting.Systemd。该软件包为在Linux下使用Systemd守护进程的应用程序提供基本配置。或者命令行:%dotnetaddpackageMicrosoft.Extensions.Hosting.Systemd3。修改Program.cs其实就是一行代码,将第二步引入的包添加到应用中。修改Program.cspublicstaticIHostBuilderCreateHostBuilder(string[]args)=>Host.CreateDefaultBuilder(args).UseSystemd()//添加这一行。.ConfigureServices((hostContext,services)=>{services.AddHostedService();});至此,常规工作已经完成。简单的?我们来看看当前项目:├──Program.cs├──Properties│└──launchSettings.json├──Worker.cs├──appsettings.Development.json├──appsettings.json└──workerdemo.csproj你会注意到有一个额外的Worker.cs类文件。看一下这个文件:publicclassWorker:BackgroundService{privatereadonlyILogger_logger;publicWorker(ILoggerlogger){_logger=logger;}protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken){while(!stoppingToken.Is{CancellationRequested){_logger.Logrunning(":time}",DateTimeOffset.Now);awaitTask.Delay(1000,stoppingToken);}}}这实际上是加载到Systemd中的服务的模板。我们需要的服务代码需要添加到ExecuteAsync(CancellationTokenstoppingToken)方法中。我简单举个例子,在里面添加UDP服务,看代码:(Ctopcellation){_logger.LogInformation("Workerrunningat:{time}",DateTimeOffset.Now);UdpClientudpClient=newUdpClient(newIPEndPoint(IPAddress.Parse("127.0.0.1"),8000));while(!stoppingToken.IsCancellationRequested){UdpReceiveResultudpReiveceawaitudpClient.ReceiveAsync();stringmessage=Encoding.UTF8.GetString(udpReceiveResult.Buffer);Console.WriteLine($"{udpReceiveResult.RemoteEndPoint.ToString()}-{message}");awaitudpClient.SendAsync(Encoding.Default.GetBytes("Got"),3,udpReceiveResult.RemoteEndPoint);}}}在这段代码中,有两点需要注意:在Program.cs中添加UseSystemd()时,IConfiguration已经被注入。因此,在本方法中可以直接引入使用。也就是说,你可以直接读取appsetting.json的内容,例如;如上所述,真正的服务响应是在ExecuteAsync(CancellationTokenstoppingToken)中。这里没有什么特别的,只是正常的写作。以上是服务器端的程序和响应。下面我简单的提出客户要求进行测试。我就不解释了,只列出步骤:创建一个项目%dotnetnewconsole-odemoclient修改Program.csstaticasyncTaskMain(string[]args){UdpClientudpClient=newUdpClient();for(inti=0;i<10000;i++){byte[]buffer=newbyte[8*1024];awaitTask.Run(()=>{udpClient.SendAsync(buffer,buffer.Length,newIPEndPoint(IPAddress.Parse("127.0.0.1"),8000));});}while(true){UdpReceiveResultudpReceiveResult=awaitudpClient.ReceiveAsync();stringmessage=Encoding.UTF8.GetString(udpReceiveResult.Buffer);Console.WriteLine($"{udpReceiveResult.RemoteEndPoint.ToString()}-{message}");}安慰。ReadKey();}运行一下看看效果。至此,Service应用开发工作已经完成。下面是部署。部署一个Service应用程序在Linux下部署一个Service应用程序只有两个步骤:1.创建一个Service定义。Linux下的每一个Service都会有一个定义文件。该文件存在于/etc/systemd/system目录中。下面我给出一个简单的Service模板:[Unit]Description=DemoProject[Service]Type=notifyExecStart=dotnet/yourfolder/yourproject.dll[Install]WantedBy=multi-user.target把这个内容保存成一个文件,比如叫demo。服务。然后将此文件复制到/etc/systemd/system并使其可执行。简单说一下这个文件的一些项目:Description,也就是服务的名称。没关系,启动时,使用文件名demo.service;Type,服务类型,使用Dotnet加载时,只能是这个类型。如果将程序编译为自包含程序,则此类型可以很简单;ExecStart,启动程序的命令,是完整路径,所以确保你能找到程序。在上面的例子中,dotnet/yourfolder/yourproject.dll是因为PATH变量支持dotnet命令。这个文件里面有很多配置项,包括定义是否需要自动重启,重启间隔时间等等,如果需要可以去这里查询。2.有两种方式启动服务。首先是刷新Service守护进程%systemctldaemon-reload刷新守护进程时,守护进程会去/etc/systemd/system目录下寻找新添加的Service文件并启动。第二种是单独启动,有一系列命令:start%systemctlstartdemo.servicestop%systemctlstopdemo.servicerestart%systemctlrestartdemo.servicequerystatus%systemctlstatusdemo.serviceum。这是服务加载和停止的地方。注意,这样加载的Service是一个完整的系统服务,不会有任何输出。如果需要调试,一种方法是添加一个文件日志,另一种方法是用另一个命令启动它:%journalctl-udnsserver.service当然,这种方法只是用来调试的。正式运行起来,应该还是上面的方法。