/// <summary> /// Send the provided parcel. /// </summary> /// <param name="messageBusConnectionConfiguration">Persistence configuration.</param> /// <param name="parcel">Parcel to send.</param> /// <param name="schedule">Optional recurring schedule.</param> public static void Send( MessageBusConnectionConfiguration messageBusConnectionConfiguration, Parcel parcel, ScheduleBase schedule = null) { new { messageBusConnectionConfiguration }.AsArg().Must().NotBeNull(); new { parcel }.AsArg().Must().NotBeNull(); var serializerFactory = SerializerFactory.Instance; var envelopeMachine = new EnvelopeMachine( PostOffice.MessageSerializerRepresentation, serializerFactory); var courier = new HangfireCourier(messageBusConnectionConfiguration.CourierPersistenceConnectionConfiguration, envelopeMachine); using (var parcelTrackingSystem = new ParcelTrackingSystem( courier, envelopeMachine, messageBusConnectionConfiguration.EventPersistenceConnectionConfiguration, messageBusConnectionConfiguration.ReadModelPersistenceConnectionConfiguration)) { var postOffice = new PostOffice(parcelTrackingSystem, HangfireCourier.DefaultChannelRouter, envelopeMachine); if (schedule == null) { postOffice.Send(parcel); } else { postOffice.SendRecurring(parcel, schedule); } } }
public static void Launch( MessageBusConnectionConfiguration messageBusConnectionConfiguration, MessageBusLaunchConfiguration launchConfig, IHandlerFactory handlerFactory) { new { messageBusConnectionConfiguration }.AsArg().Must().NotBeNull(); new { launchConfig }.AsArg().Must().NotBeNull(); new { handlerFactory }.AsArg().Must().NotBeNull(); if (launchConfig.ChannelsToMonitor.Any(_ => _.GetType() != typeof(SimpleChannel))) { throw new NotSupportedException(Invariant($"Only {nameof(SimpleChannel)}'s are supported as the implementation of {nameof(IChannel)} for {nameof(launchConfig.ChannelsToMonitor)}.")); } var assembliesToRecord = new[] { typeof(HangfireHarnessManager).Assembly }.ToList(); if (handlerFactory is ReflectionHandlerFactory reflectionHandlerFactory) { assembliesToRecord.AddRange(reflectionHandlerFactory.FilePathToAssemblyMap.Values); } var processSiblingAssemblies = assembliesToRecord.Select(SafeFetchAssemblyDetails).ToList(); var dateTimeOfSampleInUtc = DateTime.UtcNow; var machineDetails = DomainFactory.CreateMachineDetails(); var processDetails = DomainFactory.CreateProcessDetails(); var diagnosticsTelemetry = new DiagnosticsTelemetry(dateTimeOfSampleInUtc, machineDetails, processDetails, processSiblingAssemblies); var serializerFactory = SerializerFactory.Instance; var compressorFactory = CompressorFactory.Instance; var logProvider = new HangfireLogProviderToNaosLogWritingAdapter(); LogProvider.SetCurrentLogProvider(logProvider); var activeMessageTracker = new InMemoryActiveMessageTracker(); var envelopeMachine = new EnvelopeMachine( PostOffice.MessageSerializerRepresentation, serializerFactory); var courier = new HangfireCourier(messageBusConnectionConfiguration.CourierPersistenceConnectionConfiguration, envelopeMachine); var parcelTrackingSystem = new ParcelTrackingSystem( courier, envelopeMachine, messageBusConnectionConfiguration.EventPersistenceConnectionConfiguration, messageBusConnectionConfiguration.ReadModelPersistenceConnectionConfiguration); var postOffice = new PostOffice(parcelTrackingSystem, HangfireCourier.DefaultChannelRouter, envelopeMachine); HandlerToolshed.InitializePostOffice(() => postOffice); HandlerToolshed.InitializeParcelTracking(() => parcelTrackingSystem); HandlerToolshed.InitializeSerializerFactory(() => serializerFactory); HandlerToolshed.InitializeCompressorFactory(() => compressorFactory); var shareManager = new ShareManager( serializerFactory); var handlerSharedStateMap = new ConcurrentDictionary <Type, object>(); var dispatcher = new MessageDispatcher( handlerFactory, handlerSharedStateMap, launchConfig.ChannelsToMonitor, diagnosticsTelemetry, parcelTrackingSystem, activeMessageTracker, postOffice, envelopeMachine, shareManager); // configure hangfire to use the DispatcherFactory for getting IDispatchMessages calls GlobalConfiguration.Configuration.UseActivator(new DispatcherFactoryJobActivator(dispatcher)); GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = launchConfig.MessageDeliveryRetryCount }); var executorOptions = new BackgroundJobServerOptions { Queues = launchConfig.ChannelsToMonitor.OfType <SimpleChannel>().Select(_ => _.Name).ToArray(), SchedulePollingInterval = launchConfig.PollingInterval, WorkerCount = launchConfig.ConcurrentWorkerCount, }; GlobalConfiguration.Configuration.UseSqlServerStorage( messageBusConnectionConfiguration.CourierPersistenceConnectionConfiguration.ToSqlServerConnectionString(), new SqlServerStorageOptions()); var launchConfigTimeToLive = launchConfig.TimeToLive; if (launchConfigTimeToLive == default(TimeSpan)) { launchConfigTimeToLive = TimeSpan.MaxValue; } var timeout = DateTime.UtcNow.Add(launchConfigTimeToLive); // ReSharper disable once UnusedVariable - good reminder that the server object comes back and that's what is disposed in the end... using (var server = new BackgroundJobServer(executorOptions)) { Console.WriteLine(Invariant($"Hangfire Server started. Will terminate when there are no active jobs after: {timeout}.")); Log.Write(() => new { LogMessage = Invariant($"Hangfire Server launched. Will terminate when there are no active jobs after: {timeout}.") }); // once the timeout has been achieved with no active jobs the process will exit (this assumes that a scheduled task will restart the process) // the main impetus for this was the fact that Hangfire won't reconnect correctly so we must periodically initiate an entire reconnect. while (activeMessageTracker.ActiveMessagesCount != 0 || (DateTime.UtcNow < timeout)) { Thread.Sleep(launchConfig.PollingInterval); } Log.Write(() => new { ex = Invariant($"Hangfire Server terminating. There are no active jobs and current time if beyond the timeout: {timeout}.") }); } }