Azure WebJobs的优雅关闭

Geo*_*ong 6 azure-webjobs

我打算使用连续的Azure WebJob来发送电子邮件和推送通知.我知道WebJobs会因各种原因不时启动和停止.这没关系,但我希望有机会在工作关闭前"清理".

这样我的WebJob就可以更新数据库记录的状态或删除每个批处理中已处理的队列项,这样下次作业运行时就不会发送重复的消息.

作为一个黑暗的镜头,我尝试将以下代码添加到我的C#控制台应用程序:

Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) =>
{
  e.Cancel = true;
  program.keepRunning = false;
};
Run Code Online (Sandbox Code Playgroud)

然后我使用keepRunning bool来控制main while循环,并在while循环之外放置一个Console.Writeline("Exited Gracefully").但这似乎没有帮助.当我告诉作业停止时(使用我的Azure网站的Webjobs选项卡中的停止按钮),作业从列表中消失,并显示"无法停止作业:'JobName'." 在Azure门户中(位于页面底部).我没有看到WebJob日志中的"Exited Gracefully"文本.所以我把那个代码拿出来,因为它没有帮助.

所以,我正在寻找一个好方法,让我的WebJob被通知其秒数已经编号,并且需要按顺序获取其事务.

Nic*_*sen 9

我相信Amit在最后一次回答之后很快就会发生变化,事实上根据他自己的博客帖子:WebJobs Graceful Shutdown

在6:00+标记后稍微观看此视频,以便对此进行一些讨论.

来自Amit的博客:

Azure通知将要停止的进程的方式是将文件放在一个路径上,该路径作为名为WEBJOBS_SHUTDOWN_FILE的环境变量传递.

任何想要监听关闭通知的WebJob实际上都必须检查文件的存在(使用简单的File.Exists函数或使用你使用的任何脚本语言的FileSystemWatcher),当它出现时需要启动WebJob清理并打破它的当前循环,最好是它将正常退出,Azure将继续关闭(站点)进程.

好吧,这听起来不是很有趣.虽然Amit和其他人发布了一些代码来处理这个问题(见帖子),但我发现它仍然比我想要的更笨拙(我更喜欢丑陋的细节在代码中处理一次,然后立即依赖并忘记).我希望以下是一个更好的改进.我真的想要关闭设置的单行通知,这就是我们现在拥有的以下内容.我刚刚测试了这个解决方案,关闭了我的工作,它正确地解雇了.

所有工作都放在一个单独的文件/类型中,我将其命名为WebJobShutdownNotifier.首先,用法:只需在Main方法中实例化此类型,并传递具有关闭工作的void函数(或lamda).而已!它将触发你的Shutdown方法,没有太多可说的.我建议WebJobs团队直接在JobHost中加入这个或类似的东西.只需提供订阅活动即可.

用法示例:

    public static void Main() // your Main method...
    {
        // nice! a single line to handle the shutdown notification, firing your IsShuttingDown method
        var shutdownNotifier = new WebJobShutdownNotifier(IsShuttingDown);

        var host1 = new JobHost();
        host1.RunAndBlock();
    }

    public static void IsShuttingDown()
    {
        Console.WriteLine("Were shutin' down the webjob hatches baby! - {0}", DateTime.UtcNow);
        // do something else here if needed...
    }
Run Code Online (Sandbox Code Playgroud)

// --- WebJobShutdownNotifier.cs ---

using System;
using System.IO;

namespace Microsoft.Azure.WebJobs.Helper
{
    /// <summary>
    /// Base info and code adapted and expanded from Amit Apple:
    /// http://blog.amitapple.com/post/2014/05/webjobs-graceful-shutdown/.
    /// To change the wait on shutdown time from the default of 5 seconds:
    /// "create a file called settings.job with the following content: { "stopping_wait_time": 60 }""
    /// (Nicholas Petersen)
    /// </summary>
    public class WebJobShutdownNotifier
    {
        public bool IsRunning { get; private set; }

        public string ShutdownFilePath { get; private set; }

        public bool FileEnvironmentVariableExisted { get; private set; }

        /// <summary>
        /// Set this as an action allowing you to be notified when it fires that 
        /// shutdown has been triggered (/detected).
        /// </summary>
        public Action IsShuttingDownNotifier { get; set; }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="isShuttingDownNotifier">
        /// Set this as an action allowing you to be notified when it fires that 
        /// shutdown has been triggered (/detected).
        /// </param>
        public WebJobShutdownNotifier(Action isShuttingDownNotifier = null, bool exceptionIfNoFileEnvironmentVariable = false)
        {
            IsRunning = true;
            IsShuttingDownNotifier = isShuttingDownNotifier;

            // Get the shutdown file path from the environment
            ShutdownFilePath = Environment.GetEnvironmentVariable("WEBJOBS_SHUTDOWN_FILE");

            FileEnvironmentVariableExisted = !string.IsNullOrEmpty(ShutdownFilePath);

            if (!FileEnvironmentVariableExisted) {
                if (exceptionIfNoFileEnvironmentVariable)
                    throw new Exception("WEBJOBS_SHUTDOWN_FILE Environment variable returned null or empty.");
            }
            else {
                // Setup a file system watcher on that file's directory to know when the file is created
                var fileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(ShutdownFilePath));
                fileSystemWatcher.Created += OnChanged;
                fileSystemWatcher.Changed += OnChanged;
                fileSystemWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastWrite;
                fileSystemWatcher.IncludeSubdirectories = false;
                fileSystemWatcher.EnableRaisingEvents = true;
            }
        }

        private void OnChanged(object sender, FileSystemEventArgs e)
        {
            if (IsRunning) { // this was hitting more than once in the short shut down time, do not want to fire IsShuttingDownNotifier more than once...
                if (e.FullPath.IndexOf(Path.GetFileName(ShutdownFilePath), StringComparison.OrdinalIgnoreCase) >= 0) {
                    // Found the file mark, this WebJob has finished
                    IsRunning = false;
                    if (IsShuttingDownNotifier != null)
                        IsShuttingDownNotifier();
                }
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

  • 最新的Azure SDK附带了它自己的`WebJobShutdownWatcher`类. (3认同)
  • 请参阅此处https://github.com/Azure/azure-webjobs-sdk/blob/master/src/Microsoft.Azure.WebJobs.Host/WebjobsShutdownWatcher.cs (2认同)
  • @NicholasPetersen,刚刚检查并按预期工作。Azure 肯定在幕后施展了其他魔法。很高兴知道!:) (2认同)