Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【demo演示】一键发布WindowsService项目(netcore普通项目) #17

Open
yuzd opened this issue Mar 28, 2019 · 0 comments
Open

【demo演示】一键发布WindowsService项目(netcore普通项目) #17

yuzd opened this issue Mar 28, 2019 · 0 comments
Labels
good first issue Good for newcomers

Comments

@yuzd
Copy link
Owner

yuzd commented Mar 28, 2019

前提:由于是演示WindowsService项目所以得部署好agent

参考:#1
注意:Windows 服务器上 需要安装一个agent 就是 安装一个windows服务,用来处理iis的发布和windows服务的发布,可以点击上面的参考链接进行安装。
注意: 得用管理员权限部署agent 防止会出现部署到Windows 服务上权限不足问题

打开vs 选择创建一个新的netcore 控制台项目(群文件有demo工程可以下载)

image

Nuget添加如下引用

  1. Microsoft.Extensions.Hosting
  2. System.ServiceProcess.ServiceController

新建一个ServiceBaseLifetime.cs 并将下面的内容复制进去

using Microsoft.Extensions.Hosting;
using System;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp4
{

    public class ServiceBaseLifetime : ServiceBase, IHostLifetime
    {
        private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();

        public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
        {
            ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
        }

        private IApplicationLifetime ApplicationLifetime { get; }

        public Task WaitForStartAsync(CancellationToken cancellationToken)
        {
            cancellationToken.Register(() => _delayStart.TrySetCanceled());
            ApplicationLifetime.ApplicationStopping.Register(Stop);

            new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
            return _delayStart.Task;
        }

        private void Run()
        {
            try
            {
                Run(this); // This blocks until the service is stopped.
                _delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
            }
            catch (Exception ex)
            {
                _delayStart.TrySetException(ex);
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            Stop();
            return Task.CompletedTask;
        }

        // Called by base.Run when the service is ready to start.
        protected override void OnStart(string[] args)
        {
            _delayStart.TrySetResult(null);
            base.OnStart(args);
        }

        // Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
        // That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
        protected override void OnStop()
        {
            ApplicationLifetime.StopApplication();
            base.OnStop();
        }
    }
}

新建一个ServiceBaseLifetimeHostExtensions.cs 并将下面的内容复制进去

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp4
{
    public static class ServiceBaseLifetimeHostExtensions
    {
        public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
        {
            return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
        }

        public static void RunAsService(this IHostBuilder hostBuilder)
        {
            hostBuilder.UseServiceBaseLifetime().Build().Run();
        }

        public static Task RunAsServiceAsync(this IHostBuilder hostBuilder)
        {
            return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(CancellationToken.None);
        }
    }

}

新建一个服务类 TestService.cs 并写入以下内容(该服务就是每1秒往d:\log.txt写入当前时间)

using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace ConsoleApp4
{
   
    public class TestService: IHostedService,IDisposable
    {
         readonly System.Timers.Timer tmBak = new System.Timers.Timer();

        public TestService()
        {
            tmBak.Interval = 1000;//1秒执行1次
            tmBak.AutoReset = true;//执行1次false,一直执行true
            tmBak.Enabled = true;
            tmBak.Elapsed += (sender, eventArgs) =>
            {
                using (StreamWriter sw = new StreamWriter("D:\\log.txt",true))
                {
                    sw.WriteLine($"AntDeploy Windows服务:{DateTime.Now:yyyy-MM-dd HH:mm:ss}");
                }
            };
        }


        public Task StartAsync(CancellationToken cancellationToken)
        {
            tmBak.Start();
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            tmBak.Stop();
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            this.tmBak.Dispose();
        }
    }
}

编辑 Program.cs 写入如下内容:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace ConsoleApp4
{
    class Program
    {
        // P/Invoke declarations for Windows.
        [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow();
        [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr hWnd);
        public static bool HaveVisibleConsole()
        {
            return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                          IsWindowVisible(GetConsoleWindow())
                          :
                          Console.WindowHeight > 0;
        }

        private static async Task Main(string[] args)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);

            var isService = !(Debugger.IsAttached || args.Contains("--console"));

            if (HaveVisibleConsole()) isService = false;
            var builder = new HostBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<TestService>();
                });

            if (isService)
            {
                await builder.RunAsServiceAsync();
            }
            else
            {
                await builder.RunConsoleAsync();
            }
        }
    }
}

在工程上点击右键 然后选择 AntDeploy

image

配置AntDeploy

添加一个环境 名字叫 测试 然后 在 测试环境里面添加 windows服务器 这里我做测试就添加就是我本机,注意Host里面是填写格式为:ip:端口号

image

注意:Token不是windows服务器的密码!!!是安装agent后,agent的配置文件里面配置的Token(你自己自定义配置的)
注意:Port不是你要发布的项目的端口号!!!是安装agent后,agent的配置文件里面配置的端口号(你自己自定义配置的)
点击【Connect Test】按钮进行确认agent可以成功链接,否则会发布失败
如果【Connect Fail】失败 请查看 #10

进入 WindowsService Tab界面

Sdk类型选择 netcore
ServiceName 填写上面我们设置的名称:[TestService]
image

点击 【Deploy】按钮进行发布
image

确认服务器无误 点击 【是】开始执行一键部署
如果发布出现错误会出现下图所示:
image

可以在Log里面查看失败原因是因为我部署agent没有用管路员权限 报权限不足失败
image

部署成功 如下图:

image

查看D盘下是否log.txt是否正常每隔1秒写入了当前时间
image

这里演示的是windows服务上没有这个service
所以自动创建了。
如果service已存在的情况 Deploy 就会全量覆盖 不会重新创建site的。
如果想要覆盖时排除指定文件 可以在 Setting Tab界面的IgnoreList里面增加(支持正则)

@yuzd yuzd added the good first issue Good for newcomers label Mar 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

1 participant