标签:日志 Once param any 除了 dstat das throw queue
1 public IServiceProvider ConfigureServices(IServiceCollection services) 2 { 3 // 其他代码 4 services.AddHangfire(config => 5 { 6 config.UseSqlServerStorage(...); 7 }); 8 } 9 10 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 11 { 12 // 其他代码... 13 // 启用Dashboard看板 14 app.UseHangfireDashboard(); 15 }
1 public void Configuration(IAppBuilder app) 2 { 3 GlobalConfiguration.Configuration 4 .UseSqlServerStorage("连接字符串", new SqlServerStorageOptions 5 { 6 // options 7 }); 8 app.UseHangfireServer(new BackgroundJobServerOptions 9 { 10 }); 11 } 12 13
1 services.AddHangfireServer(options => 2 { 3 // 基于IHostedService接口实现 4 });
1 public interface IHostedService 2 { 3 Task StartAsync(CancellationToken cancellationToken); 4 Task StopAsync(CancellationToken cancellationToken); 5 }
1 public void Configuration(IAppBuilder app) 2 { 3 GlobalConfiguration.Configuration.UseSqlServerStorage(); // 配置数据库连接 4 5 app.UseHangfireServer(); // 启用server 6 app.UseHangfireDashboard(); // 启用看板 7 }
1 BackgroundJob.Enqueue(() => Console.WriteLine("Simple!")); // 立即执行 2 BackgroundJob.Schedule(() => Console.WriteLine("Reliable!"), TimeSpan.FromDays(7)); // 延后执行 3 RecurringJob.AddOrUpdate(() => Console.WriteLine("Transparent!"), Cron.Daily); // 循环执行,支持cron表达式
1 app.UseHangfireServer(); // 启用server
1 public static IAppBuilder UseHangfireServer( 2 [NotNull] this IAppBuilder builder, 3 [NotNull] JobStorage storage, 4 [NotNull] BackgroundJobServerOptions options, 5 [NotNull] params IBackgroundProcess[] additionalProcesses) 6 { 7 // 其他代码... 8 var server = new BackgroundJobServer(options, storage, additionalProcesses); 9 10 return builder; 11 }
1 // IBackgroundProcessingServer 2 public interface IBackgroundProcessingServer : IDisposable 3 { 4 void SendStop(); 5 bool WaitForShutdown(TimeSpan timeout); 6 Task WaitForShutdownAsync(CancellationToken cancellationToken); 7 } 8 9 // BackgroundJobServer 10 public class BackgroundJobServer : IBackgroundProcessingServer 11 { 12 // 其他成员... 13 public BackgroundJobServer( 14 [NotNull] BackgroundJobServerOptions options, 15 [NotNull] JobStorage storage, 16 [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses, 17 [CanBeNull] IJobFilterProvider filterProvider, 18 [CanBeNull] JobActivator activator, 19 [CanBeNull] IBackgroundJobFactory factory, 20 [CanBeNull] IBackgroundJobPerformer performer, 21 [CanBeNull] IBackgroundJobStateChanger stateChanger) 22 { 23 // 其他代码 24 var processes = new List<IBackgroundProcessDispatcherBuilder>(); 25 processes.AddRange(GetRequiredProcesses(filterProvider, activator, factory, performer, stateChanger)); 26 processes.AddRange(additionalProcesses.Select(x => x.UseBackgroundPool(1))); 27 var properties = new Dictionary<string, object> 28 { 29 { "Queues", options.Queues }, 30 { "WorkerCount", options.WorkerCount } 31 }; 32 33 _processingServer = new BackgroundProcessingServer( 34 storage, 35 processes, 36 properties, 37 GetProcessingServerOptions()); 38 } 39 public void SendStop() 40 { 41 } 42 public void Dispose() 43 { 44 } 45 [Obsolete("This method is a stub. There is no need to call the `Start` method. Will be removed in version 2.0.0.")] 46 public void Start() 47 { 48 } 49 [Obsolete("Please call the `Shutdown` method instead. Will be removed in version 2.0.0.")] 50 public void Stop() 51 { 52 } 53 [Obsolete("Please call the `Shutdown` method instead. Will be removed in version 2.0.0.")] 54 public void Stop(bool force) 55 { 56 } 57 public bool WaitForShutdown(TimeSpan timeout) 58 { 59 } 60 public Task WaitForShutdownAsync(CancellationToken cancellationToken) 61 { 62 }
1 public sealed class BackgroundProcessingServer : IBackgroundProcessingServer 2 { 3 // 其他成员 4 internal BackgroundProcessingServer( 5 [NotNull] BackgroundServerProcess process, 6 [NotNull] BackgroundProcessingServerOptions options) 7 { 8 _process = process ?? throw new ArgumentNullException(nameof(process)); 9 _options = options ?? throw new ArgumentNullException(nameof(options)); 10 _dispatcher = CreateDispatcher(); 11 #if !NETSTANDARD1_3 12 AppDomain.CurrentDomain.DomainUnload += OnCurrentDomainUnload; 13 AppDomain.CurrentDomain.ProcessExit += OnCurrentDomainUnload; 14 #endif 15 } 16 public void SendStop() 17 { 18 } 19 public bool WaitForShutdown(TimeSpan timeout) 20 { 21 } 22 public async Task WaitForShutdownAsync(CancellationToken cancellationToken) 23 { 24 } 25 public void Dispose() 26 { 27 28 } 29 private void OnCurrentDomainUnload(object sender, EventArgs args) 30 { 31 32 } 33 private IBackgroundDispatcher CreateDispatcher() 34 { 35 var execution = new BackgroundExecution( 36 _stoppingCts.Token, 37 new BackgroundExecutionOptions 38 { 39 Name = nameof(BackgroundServerProcess), 40 ErrorThreshold = TimeSpan.Zero, 41 StillErrorThreshold = TimeSpan.Zero, 42 RetryDelay = retry => _options.RestartDelay 43 }); 44 return new BackgroundDispatcher( 45 execution, 46 RunServer, 47 execution, 48 ThreadFactory); 49 } 50 private void RunServer(Guid executionId, object state) 51 { 52 _process.Execute(executionId, (BackgroundExecution)state, _stoppingCts.Token, _stoppedCts.Token, _shutdownCts.Token); 53 } 54 private static IEnumerable<Thread> ThreadFactory(ThreadStart threadStart) 55 { 56 yield return new Thread(threadStart) 57 { 58 IsBackground = true, 59 Name = $"{nameof(BackgroundServerProcess)} #{Interlocked.Increment(ref _lastThreadId)}", 60 }; 61 } 62 }
1 private IBackgroundDispatcher CreateDispatcher() 2 { 3 var execution = new BackgroundExecution( 4 _stoppingCts.Token, 5 new BackgroundExecutionOptions 6 { 7 Name = nameof(BackgroundServerProcess), 8 ErrorThreshold = TimeSpan.Zero, 9 StillErrorThreshold = TimeSpan.Zero, 10 RetryDelay = retry => _options.RestartDelay 11 }); 12 return new BackgroundDispatcher( 13 execution, 14 RunServer, 15 execution, 16 ThreadFactory); 17 }
1 // IBackgroundDispatcher 2 public interface IBackgroundDispatcher : IDisposable 3 { 4 bool Wait(TimeSpan timeout); 5 Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken); 6 } 7 8 // BackgroundDispatcher 9 internal sealed class BackgroundDispatcher : IBackgroundDispatcher 10 { 11 // 其他成员 12 public BackgroundDispatcher( 13 [NotNull] IBackgroundExecution execution, 14 [NotNull] Action<Guid, object> action, 15 [CanBeNull] object state, 16 [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory) 17 { 18 if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory)); 19 _execution = execution ?? throw new ArgumentNullException(nameof(execution)); 20 _action = action ?? throw new ArgumentNullException(nameof(action)); 21 _state = state; 22 #if !NETSTANDARD1_3 23 AppDomainUnloadMonitor.EnsureInitialized(); 24 #endif 25 var threads = threadFactory(DispatchLoop)?.ToArray(); 26 if (threads == null || threads.Length == 0) 27 { 28 throw new ArgumentException("At least one unstarted thread should be created.", nameof(threadFactory)); 29 } 30 if (threads.Any(thread => thread == null || (thread.ThreadState & ThreadState.Unstarted) == 0)) 31 { 32 throw new ArgumentException("All the threads should be non-null and in the ThreadState.Unstarted state.", nameof(threadFactory)); 33 } 34 _stopped = new CountdownEvent(threads.Length); 35 foreach (var thread in threads) 36 { 37 thread.Start(); 38 } 39 } 40 public bool Wait(TimeSpan timeout) 41 { 42 return _stopped.WaitHandle.WaitOne(timeout); 43 } 44 public async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken) 45 { 46 await _stopped.WaitHandle.WaitOneAsync(timeout, cancellationToken).ConfigureAwait(false); 47 } 48 public void Dispose() 49 { 50 } 51 public override string ToString() 52 { 53 } 54 private void DispatchLoop() 55 { 56 try 57 { 58 _execution.Run(_action, _state); 59 } 60 catch (Exception ex) 61 { 62 63 } 64 finally 65 { 66 try 67 { 68 _stopped.Signal(); 69 } 70 catch (ObjectDisposedException) 71 { 72 73 } 74 } 75 } 76 }
1 public interface IBackgroundExecution : IDisposable 2 { 3 void Run([NotNull] Action<Guid, object> callback, [CanBeNull] object state); 4 Task RunAsync([NotNull] Func<Guid, object, Task> callback, [CanBeNull] object state); 5 }
1 internal sealed class BackgroundExecution : IBackgroundExecution 2 { 3 // 其他成员 4 public void Run(Action<Guid, object> callback, object state) 5 { 6 if (callback == null) throw new ArgumentNullException(nameof(callback)); 7 var executionId = Guid.NewGuid(); 8 9 { 10 #if !NETSTANDARD1_3 11 try 12 #endif 13 { 14 HandleStarted(executionId, out var nextDelay); 15 while (true) 16 { 17 // Don‘t place anything here. 18 try 19 { 20 21 if (StopRequested) break; 22 if (nextDelay > TimeSpan.Zero) 23 { 24 HandleDelay(executionId, nextDelay); 25 } 26 callback(executionId, state); 27 HandleSuccess(out nextDelay); 28 } 29 #if !NETSTANDARD1_3 30 catch (ThreadAbortException) when (AppDomainUnloadMonitor.IsUnloading) 31 { 32 // Our thread is aborted due to AppDomain unload. It‘s better to give up to 33 // not to cause the host to be more aggressive. 34 throw; 35 } 36 #endif 37 catch (OperationCanceledException) when (StopRequested) 38 { 39 break; 40 } 41 catch (Exception ex) 42 { 43 HandleException(executionId, ex, out nextDelay); 44 } 45 } 46 HandleStop(executionId); 47 } 48 #if !NETSTANDARD1_3 49 catch (ThreadAbortException ex) 50 { 51 HandleThreadAbort(executionId, ex); 52 } 53 #endif 54 } 55 } 56 }
1 private void RunServer(Guid executionId, object state) 2 { 3 _process.Execute(executionId, (BackgroundExecution)state, _stoppingCts.Token, _stoppedCts.Token, _shutdownCts.Token); 4 }
1 internal interface IBackgroundServerProcess 2 { 3 void Execute( 4 Guid executionId, 5 BackgroundExecution execution, 6 CancellationToken stoppingToken, 7 CancellationToken stoppedToken, 8 CancellationToken shutdownToken); 9 }
1 internal sealed class BackgroundServerProcess : IBackgroundServerProcess 2 { 3 4 // 其他成员 5 public BackgroundServerProcess( 6 [NotNull] JobStorage storage, 7 [NotNull] IEnumerable<IBackgroundProcessDispatcherBuilder> dispatcherBuilders, 8 [NotNull] BackgroundProcessingServerOptions options, 9 [NotNull] IDictionary<string, object> properties) 10 { 11 if (dispatcherBuilders == null) throw new ArgumentNullException(nameof(dispatcherBuilders)); 12 13 14 _storage = storage ?? throw new ArgumentNullException(nameof(storage)); 15 _options = options ?? throw new ArgumentNullException(nameof(options)); 16 _properties = properties ?? throw new ArgumentNullException(nameof(properties)); 17 18 19 var builders = new List<IBackgroundProcessDispatcherBuilder>(); 20 builders.AddRange(GetRequiredProcesses()); // 添加默认的工作dispatcher也就是独立线程 21 builders.AddRange(GetStorageComponents()); 22 builders.AddRange(dispatcherBuilders); 23 24 25 _dispatcherBuilders = builders.ToArray(); 26 } 27 28 29 public void Execute(Guid executionId, BackgroundExecution execution, CancellationToken stoppingToken, 30 CancellationToken stoppedToken, CancellationToken shutdownToken) // server初始化 31 { 32 var serverId = GetServerId(); 33 Stopwatch stoppedAt = null; 34 35 36 void HandleRestartSignal() 37 { 38 if (!stoppingToken.IsCancellationRequested) 39 { 40 _logger.Info($"{GetServerTemplate(serverId)} caught restart signal..."); 41 } 42 } 43 using (var restartCts = new CancellationTokenSource()) 44 using (var restartStoppingCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, restartCts.Token)) 45 using (var restartStoppedCts = CancellationTokenSource.CreateLinkedTokenSource(stoppedToken, restartCts.Token)) 46 using (var restartShutdownCts = CancellationTokenSource.CreateLinkedTokenSource(shutdownToken, restartCts.Token)) 47 using (restartStoppingCts.Token.Register(HandleStopRestartSignal)) 48 using (stoppingToken.Register(HandleStoppingSignal)) 49 using (stoppedToken.Register(HandleStoppedSignal)) 50 using (shutdownToken.Register(HandleShutdownSignal)) 51 using (restartCts.Token.Register(HandleRestartSignal)) 52 { 53 var context = new BackgroundServerContext( 54 serverId, 55 _storage, 56 _properties, 57 restartStoppingCts.Token, 58 restartStoppedCts.Token, 59 restartShutdownCts.Token); 60 var dispatchers = new List<IBackgroundDispatcher>(); 61 CreateServer(context); 62 try 63 { 64 // ReSharper disable once AccessToDisposedClosure 65 using (var heartbeat = CreateHeartbeatProcess(context, () => restartCts.Cancel())) // 创建守护线程 66 { 67 StartDispatchers(context, dispatchers); // 启动hangfire默认初始化的所有独立任务线程 68 execution.NotifySucceeded(); 69 WaitForDispatchers(context, dispatchers); 70 71 72 restartCts.Cancel(); 73 74 heartbeat.WaitAsync(Timeout.InfiniteTimeSpan, shutdownToken).GetAwaiter().GetResult(); 75 } 76 } 77 finally 78 { 79 DisposeDispatchers(dispatchers); 80 ServerDelete(context, stoppedAt); 81 } 82 } 83 } 84 85 86 private IBackgroundDispatcher CreateHeartbeatProcess(BackgroundServerContext context, Action requestRestart) // 创建守护线程 87 { 88 return new ServerHeartbeatProcess(_options.HeartbeatInterval, _options.ServerTimeout, requestRestart) 89 .UseBackgroundPool(threadCount: 1) 90 .Create(context, _options); 91 } 92 93 94 private IEnumerable<IBackgroundProcessDispatcherBuilder> GetRequiredProcesses() // 初始化日志和任务监控线程 95 { 96 yield return new ServerWatchdog(_options.ServerCheckInterval, _options.ServerTimeout).UseBackgroundPool(threadCount: 1); 97 yield return new ServerJobCancellationWatcher(_options.CancellationCheckInterval).UseBackgroundPool(threadCount: 1); 98 } 99 private string GetServerId() // 获取serverid 100 { 101 var serverName = _options.ServerName 102 ?? Environment.GetEnvironmentVariable("COMPUTERNAME") 103 ?? Environment.GetEnvironmentVariable("HOSTNAME"); 104 var guid = Guid.NewGuid().ToString(); 105 106 return !String.IsNullOrWhiteSpace(serverName) ? $"{serverName.ToLowerInvariant()}:{guid}" : guid; 107 } 108 109 110 private void CreateServer(BackgroundServerContext context) // 创建server,写入Server数据表 111 { 112 var stopwatch = Stopwatch.StartNew(); 113 using (var connection = _storage.GetConnection()) 114 { 115 connection.AnnounceServer(context.ServerId, GetServerContext(_properties)); 116 } 117 stopwatch.Stop(); 118 119 120 ServerJobCancellationToken.AddServer(context.ServerId); 121 _logger.Info($"{GetServerTemplate(context.ServerId)} successfully announced in {stopwatch.Elapsed.TotalMilliseconds} ms"); 122 } 123 124 125 private void StartDispatchers(BackgroundServerContext context, ICollection<IBackgroundDispatcher> dispatchers) // 启动所有独立的任务线程,包括我们的队列计划、循环计划、日志、守护等等线程 126 { 127 128 foreach (var dispatcherBuilder in _dispatcherBuilders) 129 { 130 dispatchers.Add(dispatcherBuilder.Create(context, _options)); 131 } 132 } 133 134 }
1 private void StartDispatchers(BackgroundServerContext context, ICollection<IBackgroundDispatcher> dispatchers) 2 { 3 // 其他代码... 4 foreach (var dispatcherBuilder in _dispatcherBuilders) 5 { 6 dispatchers.Add(dispatcherBuilder.Create(context, _options)); // 初始化独立任务线程 7 } 8 }
1 public IBackgroundDispatcher Create(BackgroundServerContext context, BackgroundProcessingServerOptions options) // 第一步 2 { 3 // 其他代码 4 var execution = new BackgroundExecution( 5 context.StoppingToken, 6 new BackgroundExecutionOptions 7 { 8 Name = _process.GetType().Name, 9 RetryDelay = options.RetryDelay 10 }); // 定义自己的execution 11 return new BackgroundDispatcher( // 创建BackgroundDispatcher 12 execution, 13 ExecuteProcess, // 指定回调 14 Tuple.Create(_process, context, execution), // 创建三元组上下文,注意一下1元组这个对象 15 _threadFactory); 16 } 17 18 public BackgroundDispatcher( // 第二步 19 [NotNull] IBackgroundExecution execution, 20 [NotNull] Action<Guid, object> action, 21 [CanBeNull] object state, 22 [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory) 23 { 24 25 _state = state; 26 27 var threads = threadFactory(DispatchLoop)?.ToArray(); 28 29 foreach (var thread in threads) 30 { 31 thread.Start(); // 执行线程 32 } 33 } 34 35 private void DispatchLoop() // 第三步 36 { 37 try 38 { 39 _execution.Run(_action, _state); // 在run里面回调_action 40 } 41 catch (Exception ex) 42 { 43 } 44 finally 45 { 46 try 47 { 48 _stopped.Signal(); 49 } 50 catch (ObjectDisposedException) 51 { 52 } 53 } 54 } 55 56 private static void ExecuteProcess(Guid executionId, object state) // 第四步 回调方法,对应上面的指定回调 57 { 58 var tuple = (Tuple<IBackgroundProcess, BackgroundServerContext, BackgroundExecution>)state; 59 var serverContext = tuple.Item2; 60 var context = new BackgroundProcessContext( // 创建公共上下文 61 serverContext.ServerId, 62 serverContext.Storage, 63 serverContext.Properties.ToDictionary(x => x.Key, x => x.Value), 64 executionId, 65 serverContext.StoppingToken, 66 serverContext.StoppedToken, 67 serverContext.ShutdownToken); 68 while (!context.IsStopping) 69 { 70 tuple.Item1.Execute(context); // 执行自己元组对应的实例 71 tuple.Item3.NotifySucceeded(); 72 } 73 }
1 public interface IBackgroundProcess : IServerProcess 2 { 3 void Execute([NotNull] BackgroundProcessContext context); 4 }
1 public class RecurringJobScheduler : IBackgroundProcess 2 { 3 // 其他代码 4 public RecurringJobScheduler( 5 [NotNull] IBackgroundJobFactory factory, 6 TimeSpan pollingDelay, 7 [NotNull] ITimeZoneResolver timeZoneResolver, 8 [NotNull] Func<DateTime> nowFactory) 9 { 10 if (factory == null) throw new ArgumentNullException(nameof(factory)); 11 if (nowFactory == null) throw new ArgumentNullException(nameof(nowFactory)); 12 if (timeZoneResolver == null) throw new ArgumentNullException(nameof(timeZoneResolver)); 13 14 15 _factory = factory; 16 _nowFactory = nowFactory; 17 _timeZoneResolver = timeZoneResolver; 18 _pollingDelay = pollingDelay; 19 _profiler = new SlowLogProfiler(_logger); 20 } 21 22 23 /// <inheritdoc /> 24 public void Execute(BackgroundProcessContext context) // 实现方法 25 { 26 if (context == null) throw new ArgumentNullException(nameof(context)); 27 28 29 var jobsEnqueued = 0; 30 31 32 while (EnqueueNextRecurringJobs(context)) // 从数据库获取定时任务 33 { 34 jobsEnqueued++; 35 36 37 if (context.IsStopping) 38 { 39 break; 40 } 41 } 42 43 44 if (jobsEnqueued != 0) 45 { 46 _logger.Debug($"{jobsEnqueued} recurring job(s) enqueued."); 47 } 48 49 50 if (_pollingDelay > TimeSpan.Zero) 51 { 52 context.Wait(_pollingDelay); 53 } 54 else 55 { 56 var now = _nowFactory(); 57 context.Wait(now.AddMilliseconds(-now.Millisecond).AddSeconds(-now.Second).AddMinutes(1) - now); 58 } 59 } 60 }
1 private bool EnqueueNextRecurringJobs(BackgroundProcessContext context) 2 { 3 return UseConnectionDistributedLock(context.Storage, connection => 4 { 5 var result = false; 6 if (IsBatchingAvailable(connection)) 7 { 8 var now = _nowFactory(); 9 var timestamp = JobHelper.ToTimestamp(now); 10 var recurringJobIds = ((JobStorageConnection)connection).GetFirstByLowestScoreFromSet("recurring-jobs", 0, timestamp, BatchSize); // 从数据库里面查询 11 if (recurringJobIds == null || recurringJobIds.Count == 0) return false; 12 foreach (var recurringJobId in recurringJobIds) 13 { 14 if (context.IsStopping) return false; 15 if (TryEnqueueBackgroundJob(context, connection, recurringJobId, now))// 排队执行 16 { 17 result = true; 18 } 19 } 20 } 21 else 22 { 23 for (var i = 0; i < BatchSize; i++) 24 { 25 if (context.IsStopping) return false; 26 var now = _nowFactory(); 27 var timestamp = JobHelper.ToTimestamp(now); 28 var recurringJobId = connection.GetFirstByLowestScoreFromSet("recurring-jobs", 0, timestamp); 29 if (recurringJobId == null) return false; 30 if (!TryEnqueueBackgroundJob(context, connection, recurringJobId, now)) 31 { 32 return false; 33 } 34 } 35 } 36 return result; 37 }); 38 }
1 private bool EnqueueBackgroundJob( 2 BackgroundProcessContext context, 3 IStorageConnection connection, 4 string recurringJobId, 5 DateTime now) 6 { 7 // 其他代码 8 using (connection.AcquireDistributedRecurringJobLock(recurringJobId, LockTimeout)) 9 { 10 try 11 { 12 var recurringJob = connection.GetRecurringJob(recurringJobId, _timeZoneResolver, now); 13 if (recurringJob == null) 14 { 15 using (var transaction = connection.CreateWriteTransaction()) 16 { 17 transaction.RemoveFromSet("recurring-jobs", recurringJobId); 18 transaction.Commit(); 19 } 20 return false; 21 } 22 23 BackgroundJob backgroundJob = null; 24 IReadOnlyDictionary<string, string> changedFields; 25 if (recurringJob.TrySchedule(out var nextExecution, out var error)) 26 { 27 if (nextExecution.HasValue && nextExecution <= now) 28 { 29 backgroundJob = _factory.TriggerRecurringJob(context.Storage, connection, _profiler, recurringJob, now); 30 if (String.IsNullOrEmpty(backgroundJob?.Id)) 31 { 32 _logger.Debug($"Recurring job ‘{recurringJobId}‘ execution at ‘{nextExecution}‘ has been canceled."); 33 } 34 } 35 recurringJob.IsChanged(out changedFields, out nextExecution); 36 } 37 else if (recurringJob.RetryAttempt < MaxRetryAttemptCount) 38 { 39 var delay = _pollingDelay > TimeSpan.Zero ? _pollingDelay : TimeSpan.FromMinutes(1); 40 41 _logger.WarnException($"Recurring job ‘{recurringJobId}‘ can‘t be scheduled due to an error and will be retried in {delay}.", error); 42 recurringJob.ScheduleRetry(delay, out changedFields, out nextExecution); 43 } 44 else 45 { 46 _logger.ErrorException($"Recurring job ‘{recurringJobId}‘ can‘t be scheduled due to an error and will be disabled.", error); 47 recurringJob.Disable(error, out changedFields, out nextExecution); 48 } 49 50 using (var transaction = connection.CreateWriteTransaction()) 51 { 52 if (backgroundJob != null) 53 { 54 _factory.StateMachine.EnqueueBackgroundJob( 55 context.Storage, 56 connection, 57 transaction, 58 recurringJob, 59 backgroundJob, 60 "Triggered by recurring job scheduler", 61 _profiler); 62 } 63 transaction.UpdateRecurringJob(recurringJob, changedFields, nextExecution, _logger); 64 transaction.Commit(); 65 return true; 66 } 67 } 68 catch (TimeZoneNotFoundException ex) 69 { 70 catch (Exception ex) 71 { 72 73 } 74 return false; 75 } 76 }
标签:日志 Once param any 除了 dstat das throw queue
原文地址:https://www.cnblogs.com/adair-blog/p/12490042.html