/src/MassTransit.RabbitMqTransport/Scheduling/DelayedExchangeMessageScheduler.cs

http://github.com/phatboyg/MassTransit · C# · 204 lines · 150 code · 42 blank · 12 comment · 58 complexity · 4aa7180e41773b4ce479fd05eaad2857 MD5 · raw file

  1. // Copyright 2007-2016 Chris Patterson, Dru Sellers, Travis Smith, et. al.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  4. // this file except in compliance with the License. You may obtain a copy of the
  5. // License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software distributed
  10. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  11. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  12. // specific language governing permissions and limitations under the License.
  13. namespace MassTransit.RabbitMqTransport.Scheduling
  14. {
  15. using System;
  16. using System.Threading;
  17. using System.Threading.Tasks;
  18. using MassTransit.Pipeline;
  19. using MassTransit.Scheduling;
  20. using Topology;
  21. using Util;
  22. public class DelayedExchangeMessageScheduler :
  23. IMessageScheduler
  24. {
  25. readonly RabbitMqHostSettings _hostSettings;
  26. readonly ISendEndpointProvider _sendEndpointProvider;
  27. public DelayedExchangeMessageScheduler(ISendEndpointProvider sendEndpointProvider, RabbitMqHostSettings hostSettings)
  28. {
  29. _sendEndpointProvider = sendEndpointProvider;
  30. _hostSettings = hostSettings;
  31. }
  32. Task<ScheduledMessage<T>> IMessageScheduler.ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, T message, CancellationToken cancellationToken)
  33. {
  34. if (destinationAddress == null)
  35. throw new ArgumentNullException(nameof(destinationAddress));
  36. if (message == null)
  37. throw new ArgumentNullException(nameof(message));
  38. return ScheduleSend(destinationAddress, scheduledTime, message, Pipe.Empty<SendContext<T>>(), cancellationToken);
  39. }
  40. Task<ScheduledMessage<T>> IMessageScheduler.ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, T message, IPipe<SendContext<T>> pipe, CancellationToken cancellationToken)
  41. {
  42. if (destinationAddress == null)
  43. throw new ArgumentNullException(nameof(destinationAddress));
  44. if (message == null)
  45. throw new ArgumentNullException(nameof(message));
  46. if (pipe == null)
  47. throw new ArgumentNullException(nameof(pipe));
  48. return ScheduleSend(destinationAddress, scheduledTime, message, pipe, cancellationToken);
  49. }
  50. Task<ScheduledMessage<T>> IMessageScheduler.ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, T message, IPipe<SendContext> pipe, CancellationToken cancellationToken)
  51. {
  52. if (destinationAddress == null)
  53. throw new ArgumentNullException(nameof(destinationAddress));
  54. if (message == null)
  55. throw new ArgumentNullException(nameof(message));
  56. if (pipe == null)
  57. throw new ArgumentNullException(nameof(pipe));
  58. return ScheduleSend(destinationAddress, scheduledTime, message, pipe, cancellationToken);
  59. }
  60. Task<ScheduledMessage> IMessageScheduler.ScheduleSend(Uri destinationAddress, DateTime scheduledTime, object message, CancellationToken cancellationToken)
  61. {
  62. if (destinationAddress == null)
  63. throw new ArgumentNullException(nameof(destinationAddress));
  64. if (message == null)
  65. throw new ArgumentNullException(nameof(message));
  66. var messageType = message.GetType();
  67. return MessageSchedulerConverterCache.ScheduleSend(this, destinationAddress, scheduledTime, message, messageType, cancellationToken);
  68. }
  69. Task<ScheduledMessage> IMessageScheduler.ScheduleSend(Uri destinationAddress, DateTime scheduledTime, object message, Type messageType, CancellationToken cancellationToken)
  70. {
  71. if (destinationAddress == null)
  72. throw new ArgumentNullException(nameof(destinationAddress));
  73. if (message == null)
  74. throw new ArgumentNullException(nameof(message));
  75. if (messageType == null)
  76. throw new ArgumentNullException(nameof(messageType));
  77. return MessageSchedulerConverterCache.ScheduleSend(this, destinationAddress, scheduledTime, message, messageType, cancellationToken);
  78. }
  79. Task<ScheduledMessage> IMessageScheduler.ScheduleSend(Uri destinationAddress, DateTime scheduledTime, object message, IPipe<SendContext> pipe, CancellationToken cancellationToken)
  80. {
  81. if (destinationAddress == null)
  82. throw new ArgumentNullException(nameof(destinationAddress));
  83. if (message == null)
  84. throw new ArgumentNullException(nameof(message));
  85. if (pipe == null)
  86. throw new ArgumentNullException(nameof(pipe));
  87. var messageType = message.GetType();
  88. return MessageSchedulerConverterCache.ScheduleSend(this, destinationAddress, scheduledTime, message, messageType, pipe, cancellationToken);
  89. }
  90. Task<ScheduledMessage> IMessageScheduler.ScheduleSend(Uri destinationAddress, DateTime scheduledTime, object message, Type messageType, IPipe<SendContext> pipe, CancellationToken cancellationToken)
  91. {
  92. if (destinationAddress == null)
  93. throw new ArgumentNullException(nameof(destinationAddress));
  94. if (message == null)
  95. throw new ArgumentNullException(nameof(message));
  96. if (messageType == null)
  97. throw new ArgumentNullException(nameof(messageType));
  98. if (pipe == null)
  99. throw new ArgumentNullException(nameof(pipe));
  100. return MessageSchedulerConverterCache.ScheduleSend(this, destinationAddress, scheduledTime, message, messageType, pipe, cancellationToken);
  101. }
  102. Task<ScheduledMessage<T>> IMessageScheduler.ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, object values, CancellationToken cancellationToken)
  103. {
  104. if (destinationAddress == null)
  105. throw new ArgumentNullException(nameof(destinationAddress));
  106. if (values == null)
  107. throw new ArgumentNullException(nameof(values));
  108. var message = TypeMetadataCache<T>.InitializeFromObject(values);
  109. return ScheduleSend(destinationAddress, scheduledTime, message, Pipe.Empty<SendContext<T>>(), cancellationToken);
  110. }
  111. Task<ScheduledMessage<T>> IMessageScheduler.ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, object values, IPipe<SendContext<T>> pipe, CancellationToken cancellationToken)
  112. {
  113. if (destinationAddress == null)
  114. throw new ArgumentNullException(nameof(destinationAddress));
  115. if (values == null)
  116. throw new ArgumentNullException(nameof(values));
  117. if (pipe == null)
  118. throw new ArgumentNullException(nameof(pipe));
  119. var message = TypeMetadataCache<T>.InitializeFromObject(values);
  120. return ScheduleSend(destinationAddress, scheduledTime, message, pipe, cancellationToken);
  121. }
  122. Task<ScheduledMessage<T>> IMessageScheduler.ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, object values, IPipe<SendContext> pipe, CancellationToken cancellationToken)
  123. {
  124. if (destinationAddress == null)
  125. throw new ArgumentNullException(nameof(destinationAddress));
  126. if (values == null)
  127. throw new ArgumentNullException(nameof(values));
  128. if (pipe == null)
  129. throw new ArgumentNullException(nameof(pipe));
  130. var message = TypeMetadataCache<T>.InitializeFromObject(values);
  131. return ScheduleSend(destinationAddress, scheduledTime, message, pipe, cancellationToken);
  132. }
  133. Task IMessageScheduler.CancelScheduledSend(Guid tokenId)
  134. {
  135. throw new NotSupportedException("RabbitMQ delayed exchange does not support cancellation");
  136. }
  137. async Task<ScheduledMessage<T>> ScheduleSend<T>(Uri destinationAddress, DateTime scheduledTime, T message, IPipe<SendContext<T>> pipe, CancellationToken cancellationToken)
  138. where T : class
  139. {
  140. var destinationSettings = destinationAddress.GetSendSettings();
  141. var sendSettings = new RabbitMqSendSettings(destinationSettings.ExchangeName + "_delay", "x-delayed-message", destinationSettings.Durable,
  142. destinationSettings.AutoDelete);
  143. sendSettings.SetExchangeArgument("x-delayed-type", destinationSettings.ExchangeType);
  144. sendSettings.BindToExchange(destinationSettings.ExchangeName);
  145. var delayExchangeAddress = _hostSettings.GetSendAddress(sendSettings);
  146. var delayEndpoint = await _sendEndpointProvider.GetSendEndpoint(delayExchangeAddress).ConfigureAwait(false);
  147. var messageId = NewId.NextGuid();
  148. IPipe<SendContext<T>> delayPipe = Pipe.ExecuteAsync<SendContext<T>>(async context =>
  149. {
  150. context.MessageId = messageId;
  151. var rabbitSendContext = context.GetPayload<RabbitMqSendContext>();
  152. var delay = Math.Max(0,
  153. (scheduledTime.Kind == DateTimeKind.Local ? (scheduledTime - DateTime.Now) : (scheduledTime - DateTime.UtcNow)).TotalMilliseconds);
  154. if (delay > 0)
  155. rabbitSendContext.SetTransportHeader("x-delay", (long)delay);
  156. await pipe.Send(context).ConfigureAwait(false);
  157. });
  158. await delayEndpoint.Send(message, delayPipe, cancellationToken).ConfigureAwait(false);
  159. return new ScheduledMessageHandle<T>(messageId, scheduledTime, destinationAddress, message);
  160. }
  161. }
  162. }