Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

791
Vistas
Hangfire en una sola aplicación en múltiples servidores físicos

Estoy ejecutando hangfire en una sola aplicación web, mi aplicación se ejecuta en 2 servidores físicos pero hangfire está en 1 base de datos.

Por el momento, estoy generando un servidor para cada cola, porque cada cola necesito ejecutar 1 trabajador a la vez y deben estar en orden. los configuro asi

 // core services.AddHangfire(options => { options.SetDataCompatibilityLevel(CompatibilityLevel.Version_170); options.UseSimpleAssemblyNameTypeSerializer(); options.UseRecommendedSerializerSettings(); options.UseSqlServerStorage(appSettings.Data.DefaultConnection.ConnectionString, storageOptions); }); // add multiple servers, this way we get to control how many workers are in each queue services.AddHangfireServer(options => { options.ServerName = "workflow-queue"; options.WorkerCount = 1; options.Queues = new string[] { "workflow-queue" }; options.SchedulePollingInterval = TimeSpan.FromSeconds(10); }); services.AddHangfireServer(options => { options.ServerName = "alert-schedule"; options.WorkerCount = 1; options.Queues = new string[] { "alert-schedule" }; options.SchedulePollingInterval = TimeSpan.FromMinutes(1); }); services.AddHangfireServer(options => { options.ServerName = string.Format("trigger-schedule"); options.WorkerCount = 1; options.Queues = new string[] { "trigger-schedule" }; options.SchedulePollingInterval = TimeSpan.FromMinutes(1); }); services.AddHangfireServer(options => { options.ServerName = "report-schedule"; options.WorkerCount = 1; options.Queues = new string[] { "report-schedule" }; options.SchedulePollingInterval = TimeSpan.FromMinutes(1); }); services.AddHangfireServer(options => { options.ServerName = "maintenance"; options.WorkerCount = 5; options.Queues = new string[] { "maintenance" }; options.SchedulePollingInterval = TimeSpan.FromMinutes(10); });

Mi problema es que está generando múltiples colas en los servidores, con diferentes puertos. ingrese la descripción de la imagen aquí

En mi código, estoy tratando de detener la ejecución de trabajos si están en cola/reintentando, pero si el trabajo se está ejecutando en un servidor físico diferente, no se encuentra y se vuelve a poner en cola.

Aquí está el código para verificar si ya se está ejecutando

 public async Task<bool> IsAlreadyQueuedAsync(PerformContext context) { var disableJob = false; var monitoringApi = JobStorage.Current.GetMonitoringApi(); // get the jobId, method and queue using performContext var jobId = context.BackgroundJob.Id; var methodInfo = context.BackgroundJob.Job.Method; var queueAttribute = (QueueAttribute)Attribute.GetCustomAttribute(context.BackgroundJob.Job.Method, typeof(QueueAttribute)); // enqueuedJobs var enqueuedjobStatesToCheck = new[] { "Processing" }; var enqueuedJobs = monitoringApi.EnqueuedJobs(queueAttribute.Queue, 0, 1000); var enqueuedJobsAlready = enqueuedJobs.Count(e => e.Key != jobId && e.Value != null && e.Value.Job != null && e.Value.Job.Method.Equals(methodInfo) && enqueuedjobStatesToCheck.Contains(e.Value.State)); if (enqueuedJobsAlready > 0) disableJob = true; // scheduledJobs if (!disableJob) { // check if there are any scheduledJobs that are processing var scheduledJobs = monitoringApi.ScheduledJobs(0, 1000); var scheduledJobsAlready = scheduledJobs.Count(e => e.Key != jobId && e.Value != null && e.Value.Job != null && e.Value.Job.Method.Equals(methodInfo)); if (scheduledJobsAlready > 0) disableJob = true; } // failedJobs if (!disableJob) { var failedJobs = monitoringApi.FailedJobs(0, 1000); var failedJobsAlready = failedJobs.Count(e => e.Key != jobId && e.Value != null && e.Value.Job != null && e.Value.Job.Method.Equals(methodInfo)); if (failedJobsAlready > 0) disableJob = true; } // if runBefore is true, then lets remove the current job running, else it will write a "successful" message in the logs if (disableJob) { // use hangfire delete, for cleanup BackgroundJob.Delete(jobId); // create our sqlBuilder to remove the entries altogether including the count var sqlBuilder = new SqlBuilder() .DELETE_FROM("Hangfire.[Job]") .WHERE("[Id] = {0};", jobId); sqlBuilder.Append("DELETE TOP(1) FROM Hangfire.[Counter] WHERE [Key] = 'stats:deleted' AND [Value] = 1;"); using (var cmd = _context.CreateCommand(sqlBuilder)) await cmd.ExecuteNonQueryAsync(); return true; } return false; }

Cada método tiene algo así como los siguientes atributos también

 public interface IAlertScheduleService { [Hangfire.Queue("alert-schedule")] [Hangfire.DisableConcurrentExecution(60 * 60 * 5)] Task RunAllAsync(PerformContext context); }

Implementación simple de la interfaz.

 public class AlertScheduleService : IAlertScheduleService { public Task RunAllAsync(PerformContext context) { if (IsAlreadyQueuedAsync(context)) return; // guess it isnt queued, so run it here.... } }

Así es como agrego mis trabajos programados

 //// our recurring jobs //// set these to run hourly, so they can play "catch-up" if needed RecurringJob.AddOrUpdate<IAlertScheduleService>(e => e.RunAllAsync(null), Cron.Hourly(0), queue: "alert-schedule");

¿Por qué pasó esto? ¿Cómo puedo evitar que suceda?

over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

Algo así como un tiro ciego, que evita que un trabajo se ponga en cola si un trabajo ya está en cola en la misma cola. La lógica de intento y captura es bastante fea, pero no tengo una mejor idea en este momento... Además, no estoy seguro de que la lógica de bloqueo siempre impida tener dos trabajos en EnqueudState, pero debería ayudar de todos modos. Tal vez mezclar con un IApplyStateFilter.

 public class DoNotQueueIfAlreadyQueued : IElectStateFilter { public void OnStateElection(ElectStateContext context) { if (context.CandidateState is EnqueuedState) { EnqueuedState es = context.CandidateState as EnqueuedState; IDisposable distributedLock = null; try { while (distributedLock == null) { try { distributedLock = context.Connection.AcquireDistributedLock($"{nameof(DoNotQueueIfAlreadyQueued)}-{es.Queue}", TimeSpan.FromSeconds(1)); } catch { } } var m = context.Storage.GetMonitoringApi(); if (m.EnqueuedCount(es.Queue) > 0) { context.CandidateState = new DeletedState(); } } finally { distributedLock.Dispose(); } } } }

El filtro se puede declarar como en esta respuesta.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda