创建Service应用,是一个服务端开发的必会技能。

前言

说到服务端应用,最常见的就是API服务。

除此之外,还有一类应用,比方一个Socket的服务器。这类型的应用,本身没有Web层,当然也不属于API服务。

通常大家会怎么做?

不讲究的做法,就是做一个Console应用,加载到后台一直跑着。

其实,还有另外一种做法,就是把应用加载到Services里,使应用以一个Service来做响应。这样可以依托操作系统的Services管理器来进行统一管理,自动运行和故障处理。

Dotnet做Window Service的内容,网上有很多。我今天写一个在Linux下做Service的方法。

创建Linux下的Service应用

创建一个LInux下的Service应用其实很简单,就分这么几步:

1. 用 Worker 模板创建工程

如果习惯用VS上创建,就找一下Worker Service模板。

我是习惯从命令行创建,就一条命令:

% dotnet new worker -o projectname

Dotnet会自动造成工程,并自动引用Microsoft.Extensions.Hosting包,因为这本身是一个Self-Hosting应用。

2. 加入Linux Service扩展包

其实这就是一个包:Microsoft.Extensions.Hosting.Systemd。这个包为应用提供了在Linux下使用Systemd守护进程的基础配置。

还是命令行:

% dotnet add package Microsoft.Extensions.Hosting.Systemd

3. 修改Program.cs

其实就是一行代码,把第二步引入的包加入应用。修改Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).UseSystemd()  // 加入的就是这一行。.ConfigureServices((hostContext, services) =>{services.AddHostedService<Worker>();});

到这儿,套路性的工作已经完成。简单吧?

我们来看一下现在的工程:

├── Program.cs
├── Properties
│   └── launchSettings.json
├── Worker.cs
├── appsettings.Development.json
├── appsettings.json
└── workerdemo.csproj

大家会注意到,里面多了一个Worker.cs的类文件。

看一下这个文件:

public class Worker : BackgroundService
{private readonly ILogger<Worker> _logger;public Worker(ILogger<Worker> logger){_logger = logger;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){while (!stoppingToken.IsCancellationRequested){_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);await Task.Delay(1000, stoppingToken);}}
}

这其实就是加载到Systemd里的服务的模板。我们需要的服务代码,需要加到ExecuteAsync(CancellationToken stoppingToken)方法中。

我简单做个例子,在里面加入UDP服务,看代码:

public class Worker : BackgroundService
{private readonly ILogger<Worker> _logger;private readonly IConfiguration _configuration;public Worker(ILogger<Worker> logger, IConfiguration configuration){_logger = logger;_configuration = configuration;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000));while (!stoppingToken.IsCancellationRequested){UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync();string message = Encoding.UTF8.GetString(udpReceiveResult.Buffer);Console.WriteLine($"{udpReceiveResult.RemoteEndPoint.ToString()} - {message}");await udpClient.SendAsync(Encoding.Default.GetBytes("Got"), 3, udpReceiveResult.RemoteEndPoint);}}
}

这个代码中,有两件事需要注意:

  1. 在前边Program.cs中加入UseSystemd()时,已经注入了IConfiguration。因此,可以在这个方法中直接引入并使用。换句话说,就是可以直接读取例如appsetting.json的内容;

  2. 是上边提到的,真正的服务响应在ExecuteAsync(CancellationToken stoppingToken)中。这儿没什么特别的,就是正常的写法。

上面这个,是服务端的程序,是响应。

下面我简单做个客户端的请求,供测试用。就不解释了,只列出步骤:

  1. 创建一个工程

% dotnet new console -o democlient
  1. 修改Program.cs

static async Task Main(string[] args)
{UdpClient udpClient = new UdpClient();for (int i = 0; i < 10000; i++){byte[] buffer = new byte[8 * 1024];await Task.Run(() =>{udpClient.SendAsync(buffer, buffer.Length, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000));});}while (true){UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync();string message = Encoding.UTF8.GetString(udpReceiveResult.Buffer);Console.WriteLine($"{udpReceiveResult.RemoteEndPoint.ToString()} - {message}");}Console.ReadKey();
}

运行一下,看看效果。

到这里,Service应用开发的工作已经完成。

下面是部署。

部署Service应用

Linux下面部署一个Service应用,只有两个步骤:

1. 创建Service定义

Linux下的每个Service,都会有个定义文件。这个文件存在于/etc/systemd/system目录下。

下面我给出一个简单的Service模板:

[Unit]
Description=DemoProject[Service]
Type=notify
ExecStart=dotnet /yourfolder/yourproject.dll[Install]
WantedBy=multi-user.target

把这个内容保存为一个文件,例如叫demo.service。然后把这个文件复制到/etc/systemd/system下,并改为可执行。

简单说一下这个文件的一些项:

  • Description,是服务的名字。不重要,启动时,你用到的是文件名demo.service

  • Type,服务类型,使用Dotnet加载时,只能是这种类型。如果把程序编译为自包含程序,这个类型可以是simple;

  • ExecStart,启动程序的命令,是全路径的,要确保能找得到这个程序。上面例子中,dotnet /yourfolder/yourproject.dll,是因为dotnet命令是有PATH变量支持的。

这个文件的配置项有很多,包括定义是否需要自动重启、重启间隔等。如果需要,可以去这里查询。

2. 启动Service

有两种方法。

第一种是刷新Service守护

% systemctl daemon-reload

刷新守护时,守护进程会去/etc/systemd/system目录下,寻找新加入的Service文件,并启动。

第二种是单独启动,有一系列命令:

  • 启动

% systemctl start demo.service
  • 停止

% systemctl stop demo.service
  • 重启

% systemctl restart demo.service
  • 查询状态

% systemctl status demo.service

嗯。这就是服务加载和停止了。

注意,这种方式加载的Service,是完全系统的服务,会没有任何输出。

如果需要调试,一种方式是加文件日志,另一种方式是用另一个命令启动:

% journalctl -u dnsserver.service

当然,这种方式只用于调试。正式运行时,还应该是上面的方式。

这就是今天的内容,希望能帮到大家。感觉有用的话,给个三连呗~

喜欢就来个三连,让更多人因你而受益

Dotnet创建Linux下的Service应用相关推荐

  1. linux 下的 service 和systemctl 服务管理方式

    linux 下的 service  和systemctl 服务管理方式 man service 得到:执行一个system V 风格的启动脚本 service - run a System V ini ...

  2. dotnet core Linux下图片验证码解决方案

    dotnet core Linux下图片验证码解决方案 参考文章: (1)dotnet core Linux下图片验证码解决方案 (2)https://www.cnblogs.com/xxff/p/9 ...

  3. linux oracle 用户创建,LINUX下Oracle数据库用户创建方法详解

    本文实例分析了LINUX下Oracle数据库用户创建方法.分享给大家供大家参考,具体如下: 1)登录linux,以oracle用户登录(如果是root用户登录的,登录后用 su - oracle命令切 ...

  4. linux下的进程创建,Linux下进程的创建

    这篇文章主要是讲解到Linux进程的控制,包括程序和进程.守护进程.守护进程的出错处理. 1.程序和进程 程序(program)是存放在磁盘文件中的可执行文件,程序的执行实例被称为进程(process ...

  5. Linux下使用service启动jar包.md

    为了方便启动jar项目,我们可以使用xxx.service方式. 1. 执行命令,进入到system目录下: cd etc/systemd/system 2. 创建一个*.service 后缀的文件( ...

  6. OpenBSD操作系统(1)——服务管理程序rcctl 类似linux下的service或systemctl

    目录 rcctl简介 启用或禁用后台进程 启动或停止后台进程 列出进程相关的参数 列出进程默认参数 列出符合特定条件的进程 列出符合条件的进程 设置进程参数 rcctl简介 rcctl是OpenBSD ...

  7. openbsd运行Linux应用程序,OpenBSD操作系统(1)——服务管理程序rcctl 类似linux下的service或syst...

    目录 rcctl简介rcctl是OpenBSD系统上的后台进程(daemon)管理程序,功能上类似于以前RedHat Linux上的chkconfig或新版中的systemctl. 它其实是一个she ...

  8. linux虚拟机 xen,创建LINUX下XEN虚拟机

    mkdir /domU 1.创建虚拟机映像 dd if=/dev/zero of=xen.img bs=1M count=16000 dd if=/dev/zero of=xen2.img bs=1M ...

  9. linux tcp 创建,Linux下tcp服务器创建的步骤

    创建一个socket,使用函数socket() socket(套接字)实质上提供了进程通信的端点,进程通信之前,双方首先必须建立各自的一个端点,否则没有办法通信.通过socket将IP地址和端口绑定之 ...

最新文章

  1. CentOS 6.2 下samba 服务的配置
  2. Linux IPv6 地址配置
  3. Juju-Gui 安装笔记
  4. Python剑指offer:数组中重复的数字
  5. chartcontrol饼状图属性设置_温故而知新,ggplot2 饼图的几点笔记
  6. 【重庆】2021年下半年软考报考时间及通知
  7. 从东岳流体下载自带OpenFOAM的Vmware虚拟机(Ubuntu20.04+OpenFOAM),无法共享文件夹【终极解决方案】
  8. 使用java.util.prefs.Preferences代替java.util.Properties
  9. Matlab看跌期权二叉树,欧式期权二叉树MATLAB程序
  10. Weaveworks增加发布自动化和事件管理
  11. 【正点原子MP157连载】 第二十一章 DMA实验-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南
  12. sip账号服务器,如何解决“sip账号未登录” ?
  13. jQuery图片弹出Lightbox插件带轮播
  14. 淘宝天猫店铺装修问题与技巧性经验汇总
  15. 移动混合开发框架+Android原生模块化/组件化
  16. android碎片化的解决方法,Android碎片化的处理
  17. JMS入门(一)--JMS基础
  18. 转贴:谁说我会画板?
  19. 示波器表笔旁边的夹子是什么_示波器探头的地线夹子应该要靠近测量点
  20. android 音量 广播,Android平台音量调节(一)音量键调节音量

热门文章

  1. 线切割机上的DOS系统
  2. 介绍自定义JDataGrid电子表格版本公式中的函数?
  3. 广西工学院c语言试题答案,广西工学院的C语言考试试题
  4. Xamarin.Android 开发中遇到旋转屏幕错误
  5. wampServer配置WWW根目录遇到的坑
  6. jquery动态改变图片
  7. Android:源码环境编译自定义的APP到ROM(System Image)中
  8. 薛定谔的猫——.NET 4.1 中的新基类,开源Preview中
  9. Unity3D学习笔记之二资源导入以及工程管理
  10. 如何将世界时钟和时区小部件添加到您的iPhone