как поймать сигнал выхода в ядре asp. net на linux? - PullRequest
1 голос
/ 04 августа 2020
• 1000 ожидание завершения задания поймать сигнал уничтожения и выполнить некоторую чистую работу

вот мой демонстрационный код:


namespace DeveloperHelper
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var http = new SimpleHttpServer();
            var t = http.RunAsync();
            Console.WriteLine("Now after http.RunAsync();");
            AppDomain.CurrentDomain.UnhandledException += (s, e) => {
                var ex = (Exception)e.ExceptionObject;
                Console.WriteLine(ex.ToString());
                Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
            };
            AppDomain.CurrentDomain.ProcessExit +=  async (s, e) =>
            {
                Console.WriteLine("ProcessExit!");
                await Task.Delay(new TimeSpan(0,0,1));
                Console.WriteLine("ProcessExit! finished");
            };
            await Task.WhenAll(t);
        }
    }
    public class SimpleHttpServer
    {
        private readonly HttpListener _httpListener;
        public SimpleHttpServer()
        {
            _httpListener = new HttpListener();
            _httpListener.Prefixes.Add("http://127.0.0.1:5100/");
        }
        public async Task RunAsync()
        {
            _httpListener.Start();
            while (true)
            {
                Console.WriteLine("Now in  while (true)");
                var context = await _httpListener.GetContextAsync();
                var response = context.Response;

                const string rc = "{\"statusCode\":200, \"data\": true}";
                var rbs = Encoding.UTF8.GetBytes(rc);
                var st = response.OutputStream;

                response.ContentType = "application/json";
                response.StatusCode = 200;

                await st.WriteAsync(rbs, 0, rbs.Length);
                context.Response.Close();
            }
        }
    }
}

ожидайте, что он напечатает

Now in  while (true)
Now after http.RunAsync();
ProcessExit!
ProcessExit! finished

, но он выводит только

$ dotnet run
Now in  while (true)
Now after http.RunAsync();
^C%

блокирует ли async / await сигнал уничтожения, за которым должен следить eventHandler?

неожиданное исключение eventHandler не имеет вывод тоже.

есть ли signal.signal(signal.SIGTERM, func) в asp. net ядро?

1 Ответ

2 голосов
/ 05 августа 2020

Хорошо, это может быть немного затянуто, но вот оно.

Основная проблема здесь в том, что HttpListener.GetContextAsync() не поддерживает отмену через CancellationToken. Так что сложно отменить эту операцию каким-то изящным образом. Что нам нужно сделать, так это «подделать» отмену.

Стивен Туб является мастером в шаблоне async / await. К счастью для нас, он написал статью под названием Как мне отменить неотменяемые операции asyn c? . Вы можете проверить это здесь .

Я не верю в использование события AppDomain.CurrentDomain.ProcessExit. Вы можете прочитать, почему некоторые пытаются этого избежать.

Я буду использовать событие Console.CancelKeyPress .

Итак, в программном файле у меня есть настройте его так:

Program.cs

class Program
{
    private static readonly CancellationTokenSource _cancellationToken =
        new CancellationTokenSource();

    static async Task Main(string[] args)
    {
        var http = new SimpleHttpServer();
        var taskRunHttpServer = http.RunAsync(_cancellationToken.Token);
        Console.WriteLine("Now after http.RunAsync();");

        Console.CancelKeyPress += (s, e) =>
        {
            _cancellationToken.Cancel();
        };

        await taskRunHttpServer;

        Console.WriteLine("Program end");
    }
}

Я взял ваш код и добавил событие Console.CancelKeyPress и добавил CancellationTokenSource. Я также изменил ваш метод SimpleHttpServer.RunAsync(), чтобы принять токен из этого источника:

SimpleHttpServer.cs

public class SimpleHttpServer
{
    private readonly HttpListener _httpListener;
    public SimpleHttpServer()
    {
        _httpListener = new HttpListener();
        _httpListener.Prefixes.Add("http://127.0.0.1:5100/");
    }
    public async Task RunAsync(CancellationToken token)
    {
        try
        {
            _httpListener.Start();
            while (!token.IsCancellationRequested)
            {
                // ...

                var context = await _httpListener.GetContextAsync().
                    WithCancellation(token);
                var response = context.Response;

                // ...
            }
        }
        catch(OperationCanceledException)
        {
            // we are going to ignore this and exit gracefully
        }
    }
}

Вместо того, чтобы зацикливаться на true, я теперь l oop от того, сигнализируется ли токен об отмене или нет.

Еще одна довольно странная вещь - это добавление метода WithCancellation к строке _httpListener.GetContextAsync().

Этот код взят из статьи Стивена Туба выше. Я создал новый файл, который предназначен для хранения расширений для задач:

TaskExtensions.cs

public static class TaskExtensions
{
    public static async Task<T> WithCancellation<T>(
        this Task<T> task, CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource<bool>();
        using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
            if (task != await Task.WhenAny(task, tcs.Task))
                throw new OperationCanceledException(cancellationToken);
        return await task;
    }
}

Я не буду go подробно рассказывать как это работает, потому что в статье выше это прекрасно объясняется.

Теперь, когда вы поймаете сигнал CTRL + C, токен получит сигнал об отмене, который выдаст OperationCanceledException, который разбивает этот l oop . Мы ловим его, отбрасываем в сторону и выходим.

Если вы хотите продолжать использовать AppDomain.CurrentDomain.ProcessExit, вы можете - на ваш выбор ... просто добавьте код внутри Console.CancelKeyPress в это событие.

Программа завершится корректно ... ну, максимально изящно.

...