• Asp.NetCore远程自启动、重启、关闭实现


    一、背景

    NetCore作为微服务可以注册到服务中心,服务中心可以远程启动、重启、关闭该微服务

    二、实现

    1、创建一个NetCore 2.0 WebApi项目

    2、创建一个进程去管理NetCore程序进程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    public class ApplicationManager
        {
     
            private static ApplicationManager _appManager;
            private IWebHost _web;
            private CancellationTokenSource _tokenSource;
            private bool _running;
            private bool _restart;
     
            public bool Restarting => _restart;
     
            public ApplicationManager()
            {
                _running = false;
                _restart = false;
             
            }
     
            public static ApplicationManager Load()
            {
                if (_appManager == null)
                    _appManager = new ApplicationManager();
     
                return _appManager;
            }
     
            public void Start()
            {
                if (_running)
                    return;
     
                if (_tokenSource != null && _tokenSource.IsCancellationRequested)
                    return;
     
                _tokenSource = new CancellationTokenSource();
                _tokenSource.Token.ThrowIfCancellationRequested();
                _running = true;
     
                _web = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();
     
                _web.Run(_tokenSource.Token);
            }
     
            public void Stop()
            {
                if (!_running)
                    return;
     
                _tokenSource.Cancel();
                _running = false;
            }
     
            public void Restart()
            {
                Stop();
     
                _restart = true;
                _tokenSource = null;
            }
        }

      3、把ApplicationManager加入到Main中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public static void Main(string[] args)
            {
                try
                {
                    var appManager = ApplicationManager.Load();
     
                    do
                    {
                        appManager.Start();
                    while (appManager.Restarting);
     
     
                }
                catch (Exception ex)
                {
     
                }
            }

      4、在程序的ValuesController中实现重启、关闭的Api

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
     
    namespace NetCoreWebApiDemo.Controllers
    {
        [Route("api/[controller]")]
        public class ValuesController : Controller
        {
            private ApplicationManager appManager = ApplicationManager.Load();
     
            // GET api/values
            [HttpGet]
            public IEnumerable<string> Get()
            {
                return new string[] { "value1""value2" };
            }
     
            // GET api/values/5
            [HttpGet("{cmd}")]
            public string Get(string cmd)
            {
                switch (cmd)
                {
                    case "restart": appManager.Restart(); break;
                    case "stop": appManager.Stop(); break;
                    case "start": appManager.Start(); break;
                }
                return cmd;
            }
     
     
     
            // POST api/values
            [HttpPost]
            public void Post([FromBody]string value)
            {
            }
     
            // PUT api/values/5
            [HttpPut("{id}")]
            public void Put(int id, [FromBody]string value)
            {
            }
     
            // DELETE api/values/5
            [HttpDelete("{id}")]
            public void Delete(int id)
            {
            }
        }
    }

    6、给程序启动和停止加入日志标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
     
    namespace NetCoreWebApiDemo
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
     
            public IConfiguration Configuration { get; }
     
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
            }
     
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
     
                app.UseMvc();
     
                lifetime.ApplicationStarted.Register(OnStart);//1:应用启动时加载配置,2:应用启动后注册服务中心
                lifetime.ApplicationStopped.Register(UnRegService);//应用停止后从服务中心注销
            }
            private void OnStart()
            {
                LoadAppConfig();
                RegService();
            }
            private void LoadAppConfig()
            {
                //加载应用配置
                Console.WriteLine("ApplicationStarted:LoadAppConfig");
            }
     
            private void RegService()
            {
                //先判断是否已经注册过了
                //this code is called when the application stops
                Console.WriteLine("ApplicationStarted:RegService");
            }
            private void UnRegService()
            {
                //this code is called when the application stops
                Console.WriteLine("ApplicationStopped:UnRegService");
            }
        }
    }

      

      

    5、在程序根目录运行dotnet run

    访问:http://localhost:51062/api/values,显示:["Value1","Value2"]

    访问:http://localhost:51062/api/values/restart:显示restart,再访问http://localhost:51062/api/values正常返回["Value1","Value2"]

     

    访问:http://localhost:51062/api/values/stop,显示:stop,再访问http://localhost:51062/api/values就是404了

    stop后由于netcore进程已经被关闭,没有了http监听,通过url方式是无法重新启动了,这里可以借助类似supervisor的工具来停止进程,启动进程。

    三、源码地址

     https://github.com/andrewfry/AspNetCore-App-Restart

    四、其它实现

    1、除了上面,还可以通过中间件的形式,实现远程关闭

    新增一个中间件的类:

    复制代码
    public class RemoteStopMiddleware
        {
            private RequestDelegate _next;
            private const string RequestHeader = "Stop-Application";
            private const string ResponseHeader = "Application-Stopped";
    
            public RemoteStopMiddleware(RequestDelegate next)
            {
                _next = next;   
            }
        
           public async Task Invoke(HttpContext context, IApplicationLifetime lifetime)
           {
               if (context.Request.Method == "HEAD" && context.Request.Headers[RequestHeader].FirstOrDefault() == "Yes")
              {
                   context.Response.Headers.Add(ResponseHeader, "Yes");
                 lifetime.StopApplication();
               }
               else if (context.Request.Method == "HEAD" && context.Request.Headers[RequestHeader].FirstOrDefault() == "No")
                {
                    context.Response.Headers.Add(ResponseHeader, "No");
                    // See you on the next request.
                    //Program.Shutdown();
                }
                else
               {
                   await _next(context);
               }
           }
        }
    复制代码

    2、注册中间件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
     
                app.UseMvc();
                app.UseMiddleware<RemoteStopMiddleware>();
                lifetime.ApplicationStarted.Register(OnStart);//1:应用启动时加载配置,2:应用启动后注册服务中心
                lifetime.ApplicationStopped.Register(UnRegService);//应用停止后从服务中心注销
            }

    3、运行程序,用postman发起一个head请求,请求头中加

    {

    Stop-Application:Yes

    }

    详细说明可参考:https://www.cnblogs.com/artech/p/application-life-time.html

     
  • 相关阅读:
    MS SQLSERVER 第三天
    MS SQLSERVER 第二天
    今天开始我的 MSSQLSERVER 之旅
    从今天开始就正式我的博客之旅
    mac 本地搭建mybatisGenerator代码生成环境
    idea中git远程版本回退
    Junit调试解决本地多线程异步调用
    Lambda表达式总结
    JDK8函数式编程之Stream API
    MySql分页查询慢的解决方案
  • 原文地址:https://www.cnblogs.com/webenh/p/15797935.html
Copyright © 2020-2023  润新知