.Net开发,在生产过程中还是有一定用户群的,最近研究了下使用VS2019环境,使用Core开发webApi并发布到Linux(CenOS 7)上,过程不算复杂,简单做个备忘!

一、创建Core WebApi 项目

 

 

 

 

 Api项目默认文件如上图,

Properties下的launchSettings.json文件

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:38382",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "WebApiTest": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

对于这个文件,需要清楚,他是调试环境的配置文件,并不是配置你的实际路由。VS对CoreWebApi提供两种调试方式,一种是IIS Express,另一种是项目自带的Kestrel web应用调式,当你用IIS Express方式调试的时候,会用到下面这个配置,launchUrl就是调试时浏览器使用的路由信息,

 当你用项目自带的Kestrel web应用调式,会使用下图的配置

 而这些配置,在你发布后,不会被编译,说这些是提前讲一下,如果发布后再部署,路由信息和网络端口都不是用这个配置文件中配置的!

Program.cs文件

Api的入口方法

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    //默认没有下面这行代码,这行代码是为了指明http监听的端口!
                    webBuilder.UseUrls("http://*:8085");
                    webBuilder.UseStartup<Startup>();
                });
    }
}

注意:理解下上面代码中的注释信息,未指明端口时,端口为调试配置文件里的配置,

当你的代码在这里指明这个网络监听端口的配置信息了,调试就用这个端口配置了

Controllers/WeatherForecastController.cs文件

这个文件是项目框架的一个默认api例子,可以直接删除掉这个文件,自己添加一个控制器(后面介绍控制器的添加和配置);

默认项目中还有个WeatherForecast.cs文件,相当于一个实体类,也可以直接删除;

配置文件中只保留WebApiTest一个配置,其他配置也删除如下

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "WebApiTest": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "api/helloworld",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

添加自己的控制器Helloworld.cs

 

 

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApiTest.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HelloworldController : ControllerBase
    {
    }
}

修改代码如下:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApiTest.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HelloworldController : ControllerBase
    {
        [Route("get")]
        [HttpGet]
        public ContentResult gt()
        {
            return Content("This is Get!");
        }
        [Route("post")]
        [HttpPost]
        public ContentResult pst()
        {
            return Content("This is Post!");
        }
    }
}

上面代码解析:

[Route("api/[controller]")] 特性指明这个控制器HellowworldController的总路由为api/helloworld
[Route("get")]特性指明gt()方法的路由是get
[HttpGet]特性指明gt()方法只能get访问不能post等其他方式访问
[Route("post")]特性指明pst()方法的路由是post
[HttpPost]特性指明pst()方法只能post访问不能get等其他方式访问

修改调试配置文件如下:

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "WebApiTest": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "api/helloworld/get",
      "applicationUrl": "http://localhost:8085",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

 现在可以进行调试了

 运行效果如下两张图

 控制台程序窗口中显示了api服务的运行情况;如果这是在vs上停止服务,默认控制台窗口不会关闭,可以通过勾选   工具----选项----调试----调试停止时自动关闭控制台  设置自动关闭;

二、跨平台发布

鼠标右键点击项目----发布

 

上图中选择Release会使编译后的文件比较小,因为Debug会将引用到的包整体编译,而Release只编译引用到的包中的具体方法;

目标框架根据自己需要选,注意:选择什么版本,Linux上就要安装相应的DotNet版本环境;

目标运行时选择可移植;

勾选在发布前删除所有现有文件,指的是发布目录;

 

 

 以上发布完成

三、部署到Linux(CentOS 7)

1、XShell连接上目标服务器

2、安装依赖包:

执行命令:sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm

3、安装SDK:

执行命令:sudo yum install dotnet-sdk-3.1

 4、确认安装成功:

 

5、创建程序目录

mkdir /var/www
mkdir /var/www/dotnet

6、使用Xftp(其他工具也可以),将上一步的publish文件夹复制到服务器上,这里我是放在了/var/www/dotnet目录下;

7、启动服务

 使用dotnet命令启动服务;

8、制作服务启动文件

如上图是启动成功后返回的信息,服务监听端口为8085;看到上图最后一行为空行,并没有退回到命令输入状态,是因为服务启动命令是窗口级的,当我们关闭当前命令窗口,或者使用Ctrl+C快捷键退回到命令输入状态时,服务又会停止;解决这个问题的办法有多种,最简单的是在程序目录publish文件夹中创建一个start.sh的文件,里面输入一行命令:

nohup dotnet WebApiTest.dll >/dev/null 2>c.log &

然后执行这个命令就可以了

 上面第一行命令是给权限,第二行是执行命令;

9、端口冲突

我们上面的例子使用的是8085端口,如果这个端口没占用,使用dotnet ./publish/WebApiTest.dll命令启动的时候会报错:

10、停止服务:

执行命令

ps -ef | grep WebApiTest.dll | grep -v grep | awk '{print $2}' | xargs kill 9

这样写如果只有一个服务匹配上WebApiTest.dll,是可以的,但是实际情况,这么干风险太大,因为这可能匹配出多个进程的pid,试想我有一个服务NotNetWebApiTest.dll使用8080端口,这个命令会匹配出两个pid,所以要修改过滤条件匹配的正则表达式,因为dotnet文件命名规则决定只能是字母数字和‘_’,我们可能将命令修改为

ps -ef | grep [^a-zA-Z0-9_]WebApiTest.dll | grep -v grep | awk '{print $2}' | xargs kill 9

11、制作服务停止文件

这样我们就可以按照创建start.sh的方法创建一个stop.sh文件,将上面的命令复制粘贴到这个文件中保存关闭文件,然后使用下面命令给文件权限

chmod +x stop.sh

使用

./stop.sh 

停止服务

12、服务开机自启动

一切都运行正常了,我们就面对一种生产场景,当我们的api越来越多,每次重启服务器就需要手动启动各种服务,遗漏任何一个都带来一定的麻烦,我们就需要将我们的所有服务配置成开机自启动;

方法有多种,如果是通过Nginx启动服务请参见Nginx启动配置,此处只介绍Linux配置开机自启动的一种方式,就是配置/etc/rc.d/rc.local文件的方式,

1)、编辑这个文件,在最后添加一行命令

nohup dotnet /var/www/dotnet/publish/WebApiTest.dll >/dev/null 2>/var/www/dotnet/publish/c.log &

2)、给这个文件可执行权限

chmod +x /etc/rc.d/rc.local

3)确保/etc/rc.d/rc.local在开机时候成功启动

重启服务器,测试服务是否正常启动,如果可以中场启动就没有问题了,如果启动失败,可以通过

查看 /var/log/messages文件,确认问题并解决,如果我们给这个文件可执行权限了,

(可以通过ls -la命令确认是否有可执行权限,文件名为绿色为可执行)

剩下常见启动失败有3种原因:

(一)是因为使用三方工具对文件进行编辑,导致文件的换行符不是CR+LF,而是LF,可以通过命令解决

sed -i "s/\r//" /etc/rc.d/rc.local

(二)是因为这个文件没有设置软连接,使用命令解决

ln -s /etc/rc.d/rc.local /etc/rc.local

也就类似在/etc/里面生成了一个“快捷方式”,如果已经存在这个“快捷方式”,这个命令会提示已经存在,可以删除后重新执行,

(三)是因为命令中没有使用绝对路径

/etc/rc.d/rc.local文件最后一行的命令一定要使用绝对路径

这时,我们想到之前做的start.sh文件,能不能直接使用,是可以的,我的做法是在rc.d目录先创建一个dotnet.autorun.scripts文件夹,把之前的start.sh文件复制过来,并修改里面的dll路径为绝对路径/var/www/dotnet/publish/WebApiTest.dll并保存,

助医日志文件c.log的路径可以在当前文件夹,方便查找,创建好这个start.sh文件别忘记之前说的需要给可执行的权限:

chmod +x /etc/rc.d/dotnet.autorun.script/start.sh

 

这样做的好处是可以把所有的服务在这一个位置配置,发布新服务可以在这个位置添加配置,并将多个服务的启动日志放在一个log文件中,方便排查启动故障 

        以上是使用VS2019创建Dotnet Core WevAPI,并发布到Linux(CentOS 7)上的全过程,如有错漏欢迎指正!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐