1. 程式人生 > >netcore開發windows普通服務(非Web)並一鍵發布到服務器

netcore開發windows普通服務(非Web)並一鍵發布到服務器

重新 格式 use otherwise wait cor url imei gis

如何開發並一鍵發布WindowsService項目(netcore普通項目)

netcore下開發windows服務如果是web項目的話,由於aspnetcore本身是支持的,把默認的host.Run改為host.RunAsService就可以了。

但是普通的netcore的控制臺項目我終於找到了如下方式來實現:

1. 打開vs 選擇創建一個新的netcore 控制臺項目

技術分享圖片

Nuget添加如下引用

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

新建一個ServiceBaseLifetime.cs 並將下面的內容復制進去

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

    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 並將下面的內容復制進去

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寫入當前時間)

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 寫入如下內容,保證既可以作為控制臺打開又可以作為windows服務運行:

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

AntDeploy是我開發的一款開源一鍵部署vs插件(也是支持脫離vs單獨使用的一個開源工具)

開源地址:https://github.com/yuzd/AntDeployAgent

技術分享圖片

配置AntDeploy

添加一個環境 名字叫 測試 然後 在 測試環境裏面添加 windows服務器 這裏我做測試就添加就是我本機,註意Host裏面是填寫格式為:ip:端口號

技術分享圖片

註意:Token不是windows服務器的密碼!!!是安裝agent後,agent的配置文件裏面配置的Token(你自己自定義配置的)
註意:Port不是你要發布的項目的端口號!!!是安裝agent後,agent的配置文件裏面配置的端口號(你自己自定義配置的)
點擊【Connect Test】按鈕進行確認agent可以成功鏈接,否則會發布失敗
如果【Connect Fail】失敗 請查看 #10

進入 WindowsService Tab界面

Sdk類型選擇 netcore
ServiceName 填寫上面我們設置的名稱:[TestService]

技術分享圖片

點擊 【Deploy】按鈕進行發布

技術分享圖片

確認服務器無誤 點擊 【是】開始執行一鍵部署
如果發布出現錯誤會出現下圖所示:

技術分享圖片

可以在Log裏面查看失敗原因是因為我部署agent沒有用管路員權限 報權限不足失敗 需要用管理員權限運行agent才行

技術分享圖片

部署成功 如下圖:

技術分享圖片

查看D盤下是否log.txt是否正常每隔1秒寫入了當前時間

技術分享圖片

這裏演示的是windows服務上沒有這個service
所以自動創建了。
如果service已存在的情況 Deploy 就會全量覆蓋 不會重新創建site的。
如果想要覆蓋時排除指定文件 可以在 Setting Tab界面的IgnoreList裏面增加(支持正則)

netcore開發windows普通服務(非Web)並一鍵發布到服務器