/// <summary> /// Creates and returns a new brokered message based on the specified message descriptor. /// </summary> /// <param name="messageDescriptor">The <see cref="IMessageDescriptor">message descriptor</see> to prepare.</param> /// <returns>A new, prepared <see cref="BrokeredMessage">message</see>.</returns> protected virtual BrokeredMessage ToBrokeredMessage(IMessageDescriptor messageDescriptor) { Arg.NotNull(messageDescriptor, nameof(messageDescriptor)); Contract.Ensures(Contract.Result <BrokeredMessage>() != null); var stream = new MemoryStream(); var writer = new StreamWriter(stream, UTF8); Configuration.JsonSerializer.Serialize(writer, messageDescriptor.Message); writer.Flush(); stream.Position = 0L; var brokeredMessage = new BrokeredMessage(stream, ownsStream: true) { MessageId = messageDescriptor.MessageId, ContentType = "application/json", CorrelationId = messageDescriptor.Message.CorrelationId, Label = messageDescriptor.Message.GetType().Name, Properties = { ["Type"] = messageDescriptor.MessageType, ["Revision"] = messageDescriptor.Message.Revision, }, }; if (messageDescriptor.AggregateId != null) { brokeredMessage.Properties["AggregateId"] = messageDescriptor.AggregateId; } return(brokeredMessage); }
/// <summary> /// Occurs when a message is received by the bus. /// </summary> /// <param name="messageDescriptor">The received <see cref="IMessageDescriptor">message descriptor</see>.</param> /// <param name="cancellationToken">The <see cref="CancellationToken">token</see> that can be used to cancel the operation.</param> /// <returns>The <see cref="Task">task</see> representing the asynchronous operation.</returns> protected virtual async Task OnMessageReceived(IMessageDescriptor messageDescriptor, CancellationToken cancellationToken) { Arg.NotNull(messageDescriptor, nameof(messageDescriptor)); var context = NewMessageContext(commandSender, this, cancellationToken); await dispatcher.Dispatch(messageDescriptor.Message, context).ConfigureAwait(false); }
public void OnNext(IMessageDescriptor value) { foreach (var observer in observers) { observer.OnNext(value); } }
/// <summary> /// Sends a message. /// </summary> /// <param name="messageSender">The extended <see cref="IMessageSender"/>.</param> /// <param name="message">The <see cref="IMessageDescriptor">message</see> to send.</param> /// <param name="cancellationToken">A <see cref="CancellationToken">token</see> that can be used to cancel the operation.</param> /// <returns>A <see cref="Task">task</see> representing the asynchronous operation.</returns> public static Task Send(this IMessageSender messageSender, IMessageDescriptor message, CancellationToken cancellationToken) { Arg.NotNull(messageSender, nameof(messageSender)); Arg.NotNull(message, nameof(message)); return(messageSender.Send(new[] { message }, cancellationToken)); }
public async ValueTask StoreAsync(IMessageDescriptor descriptor, IMessage message, CancellationToken cancellationToken = default) { if (descriptor == null) { throw new System.ArgumentNullException(nameof(descriptor)); } if (message == null) { throw new System.ArgumentNullException(nameof(message)); } var hash = await _messageHasher.HashAsync(descriptor, message, cancellationToken : cancellationToken); string messageString = null; try { if (descriptor is IRichMessageDescriptor rich && rich.Raw != null && rich.Raw.Length != 0) { messageString = Encoding.UTF8.GetString(rich.Raw); } else { messageString = _messageConverter.SerializeString(message); } }
public void Publish(IMessageDescriptor descriptor, byte[] message) { if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } using (var channel = _persistentConnection.CreateModel()) { channel.ExchangeDeclare(exchange: descriptor.MessageGroup, durable: true, type: "topic"); var properties = channel.CreateBasicProperties(); properties.DeliveryMode = 2; // persistent if (descriptor is IRichMessageDescriptor rich && rich.Headers != null) { var headers = properties.Headers = properties.Headers ?? new Dictionary <string, object>(); foreach (var item in rich.Headers) { headers.Add(item.Key, item.Value); } } channel.BasicPublish(exchange: descriptor.MessageGroup, routingKey: descriptor.MessageTopic, mandatory: true, basicProperties: properties, body: message); } }
public Type Resolve(IMessageDescriptor descriptor, IEnumerable <Type> types) { if (descriptor == null) { return(null); } if (string.IsNullOrWhiteSpace(descriptor.MessageTopic)) { return(null); } if (types == null) { return(null); } var messageType = types.Select(type => new { Type = type, Descriptor = Resolve(type) }) .FirstOrDefault(x => MessageDescriptorEqualityComparer.Instance.Equals(x.Descriptor, descriptor)) ?.Type; return(messageType); }
protected async ValueTask <bool> IsExistsAsync(IMessageDescriptor descriptor, IMessage message, CancellationToken cancellationToken = default) { var hash = await _messageHasher.HashAsync(descriptor, message, cancellationToken : cancellationToken); var model = await _storageProvider.FindAsync(hash, cancellationToken); return (string.Equals(model.Group, descriptor?.MessageGroup) && string.Equals(model.Topic, descriptor?.MessageTopic)); }
public async ValueTask <bool> IsConsumedAsync(IMessageDescriptor descriptor, IMessage message, CancellationToken cancellationToken = default) { if (descriptor == null) { throw new System.ArgumentNullException(nameof(descriptor)); } if (message == null) { throw new System.ArgumentNullException(nameof(message)); } return(await base.IsExistsAsync(descriptor, message, cancellationToken)); }
/// <summary> /// Saves the specified event descriptor using the provided command. /// </summary> /// <typeparam name="TKey">The type of key.</typeparam> /// <param name="command">The <see cref="DbCommand">database command</see> used to save the event.</param> /// <param name="eventDescriptor">The <see cref="EventDescriptor{TKey}">event descriptor</see> that /// describes the event being saved.</param> /// <param name="cancellationToken">The <see cref="CancellationToken">token</see> that can be used to cancel the operation.</param> /// <returns>A <see cref="Task">task</see> representing the asynchronous operation.</returns> public virtual Task SaveEvent <TKey>(DbCommand command, EventDescriptor <TKey> eventDescriptor, CancellationToken cancellationToken) { Arg.NotNull(command, nameof(command)); Arg.NotNull(eventDescriptor, nameof(eventDescriptor)); IMessageDescriptor messageDescriptor = eventDescriptor; command.Parameters["AggregateId"].Value = eventDescriptor.AggregateId; command.Parameters["Version"].Value = eventDescriptor.Event.Version; command.Parameters["Sequence"].Value = eventDescriptor.Event.Sequence; command.Parameters["Type"].Value = messageDescriptor.MessageType; command.Parameters["Revision"].Value = eventDescriptor.Event.Revision; command.Parameters["Message"].Value = EventSerializer.Serialize(eventDescriptor.Event); return(command.ExecuteNonQueryAsync(cancellationToken)); }
public MessageContext(Type messageType, IMessageDescriptor descriptor, IServiceProvider serviceProvider) { _messageType = messageType; _serviceProvider = serviceProvider; Handlers = ResolveHandlers(descriptor.Handlers).ToLazyReadOnlyCollection(); IndirectHandlers = ResolveHandlers(descriptor.IndirectHandlers).ToLazyReadOnlyCollection(); PostHandlers = ResolvePostHandlers(descriptor.PostHandlers).ToLazyReadOnlyCollection(); IndirectPostHandlers = ResolvePostHandlers(descriptor.IndirectPostHandlers).ToLazyReadOnlyCollection(); PreHandlers = ResolvePreHandlers(descriptor.PreHandlers).ToLazyReadOnlyCollection(); IndirectPreHandlers = ResolvePreHandlers(descriptor.IndirectPreHandlers).ToLazyReadOnlyCollection(); ErrorHandlers = ResolveErrorHandlers(descriptor.ErrorHandlers).ToLazyReadOnlyCollection(); IndirectErrorHandlers = ResolveErrorHandlers(descriptor.IndirectErrorHandlers).ToLazyReadOnlyCollection(); }
/// <summary> /// Subscribe on the message broadcasting. /// </summary> /// <param name="descriptor">Message instance. Same instance must be used for the broadcasting (through @ref Broadcast)</param> /// <param name="handler">Handler that should be invoked when <see cref="Broadcast"/> will be called.</param> /// <param name="owner">Used only for development/debug prurposes. String representation of this parameter (if specified) will be displayed withing MessageViewer</param> /// <returns></returns> public ISubscription AddSubscription(IMessageDescriptor descriptor, Action <object[]> handler, object owner) { var messageType = descriptor.GetType(); MessageDelegate messageDelegate = null; lock (m_messageDelegates) { messageDelegate = m_messageDelegates.Find(m => m.MessageType == messageType); if (messageDelegate == null) { messageDelegate = new MessageDelegate(messageType); m_messageDelegates.Add(messageDelegate); } } return(messageDelegate.AddSubscription(handler, owner)); }
public IMessage Deserialize(IMessageDescriptor descriptor, byte[] message) { if (descriptor == null) { return(null); } if (string.IsNullOrWhiteSpace(descriptor.MessageTopic)) { return(null); } if (message == null || message.Length == 0) { return(null); } var allMessageTypes = _messageHandlerFactoryStore.GetAllHandledMessageTypes(); var type = _messageTopicResolver.Resolve(descriptor, allMessageTypes); if (type == null) { return(null); } if (!typeof(IMessage).IsAssignableFrom(type)) { return(null); } var stringMessage = Encoding.UTF8.GetString(message); IMessage typedMessageObject = null; try { // typedMessageObject = JsonConvert.DeserializeObject(stringMessage, type) as IMessage; typedMessageObject = JsonSerializer.Deserialize(stringMessage, type) as IMessage; } catch (System.Exception e) { _logger.LogError(e, "反序列化消息失败"); } return(typedMessageObject); }
/// <summary> /// Broadcast message: invoke corresponding handlers with passed (possible partially) parameters. /// </summary> /// <param name="instance">Message instance. Same instance must be used for subscription (through @ref AddSubscription).</param> /// <param name="parameters">Pass directly</param> public void Broadcast(IMessageDescriptor instance, params object[] parameters) { var messageType = instance.GetType(); var messageDelegate = m_messageDelegates.Find(d => d.MessageType == messageType); if ( messageDelegate != null && messageDelegate.Invoke(parameters)) { return; } var descAttrib = ReflectionUtils.GetCustomAttribute <MessageDescriptionAttribute>(messageType); var handlerRequirement = descAttrib == null ? HandlerRequirement.Default : descAttrib.HandlerRequirement; switch (handlerRequirement) { case HandlerRequirement.Default: { //#todo add ConsoleOutput Utility #if UNITY_EDITOR UnityEngine.Debug.LogWarningFormat("There is no registered handlers for [{0}]", messageType); #endif break; } case HandlerRequirement.NotRequired: { break; } case HandlerRequirement.Required: { throw new Exception(string.Format("Message '{0}' must be handled by its requirement ! ", messageType)); } } }
public async ValueTask <string> HashAsync( IMessageDescriptor descriptor, IMessage message, HashAlgorithmName algorism = default, CancellationToken cancellationToken = default) { algorism = (algorism == default) ? HashAlgorithmName.SHA1 : algorism; byte[] raw = null; if (descriptor is IRichMessageDescriptor rich) { if (rich.Raw != null && rich.Raw.Length != 0) { raw = rich.Raw; } else if (!string.IsNullOrWhiteSpace(rich.MessageId)) { raw = Encoding.UTF8.GetBytes(rich.MessageId); } } if (raw == null) { try { raw = _messageConverter.Serialize(message); } catch (Exception) { } } if (raw == null) { return("SRC-ERROR"); } return(await Task.Run(async() => await HashBodyAsync(descriptor, raw, algorism, cancellationToken))); }
public async ValueTask <string> HashBodyAsync( IMessageDescriptor descriptor, byte[] messageBody, HashAlgorithmName algorism = default, CancellationToken cancellationToken = default) { algorism = (algorism == default) ? HashAlgorithmName.SHA1 : algorism; return(await Task.Run(() => { try { using (var hasher = HashAlgorithm.Create(algorism.Name)) { var hash = hasher.ComputeHash(messageBody); var hashString = string.Join("", hash.Select(b => b.ToString("x2"))); return hashString; } } catch (Exception e) { return $"HASH-ERROR: {e.Message}"; } })); }
private IModel CreateConsumerChannel(IMessageDescriptor descriptor) { const string MODE = "topic"; if (_channels.TryGetValue(descriptor, out var exists)) { Logger.LogWarning($"CreateConsumerChannel: Channel is already created"); return(exists); } var channel = _persistentConnection.CreateModel(); void onModelShutdown(object sender, ShutdownEventArgs eventArgs) { var model = sender as IModel; switch (eventArgs.Initiator) { case ShutdownInitiator.Application: if (eventArgs.ReplyCode == 200) { model.ModelShutdown -= onModelShutdown; Logger.LogInformation($"CreateConsumerChannel: channel { model.ChannelNumber} shutdown by app."); _channels.TryRemove(descriptor, out var _); return; } break; case ShutdownInitiator.Library: if (eventArgs.ReplyCode == 541) //Unhandled Exception { var shutdownReason = (eventArgs.Cause as Exception).Message; Logger.LogWarning($"CreateConsumerChannel: channel { model.ChannelNumber} shutdown, Initiator: {eventArgs.Initiator}, caused by :{(eventArgs.Cause as Exception).Message}"); } break; case ShutdownInitiator.Peer: break; default: break; } } channel.ModelShutdown += onModelShutdown; channel.ExchangeDeclare(exchange: descriptor.MessageGroup, durable: true, type: MODE); var queueName = _messageBusOptions.QueuePerConsumer != false ? string.Concat(_messageBusOptions.QueueName, "-", descriptor.MessageTopic) : _messageBusOptions.QueueName; channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null); channel.QueueBind(queue: queueName, exchange: descriptor.MessageGroup, routingKey: descriptor.MessageTopic); var consumer = new AsyncEventingBasicConsumer(channel); consumer.Shutdown += ConsumerShutdown; consumer.Received += ConsumerMessageReceived; var consumerTag = channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer); Logger.LogInformation($"CreateConsumerChannel: Exchange: {descriptor.MessageGroup}, RoutingKey: {descriptor.MessageTopic}, Mode:{MODE}, Queue: {queueName}, ConsumerTag: {consumerTag}"); _channels.TryAdd(descriptor, channel); return(channel); }
public ValueTask PublishAsync(IMessageDescriptor descriptor, byte[] message) { return(new ValueTask(Task.Run(() => Publish(descriptor, message)))); }
/// <summary> /// Default ctor. /// </summary> /// <param name="topic">话题</param> /// <param name="message">消息</param> public MessageWrapper(IMessageDescriptor descriptor, IMessage message) { this.Descriptor = descriptor; this.Message = message; }
public void Subscribe(IMessageDescriptor descriptor, Action <IMessage, IRichMessageDescriptor> handler) => Subscribe(descriptor, (message, _descriptor) => { handler?.Invoke(message, _descriptor); return(new ValueTask()); });
public void UnSubscribe(IMessageDescriptor descriptor) { _subscribers.TryRemove(descriptor, out var value); }
public void Subscribe(IMessageDescriptor descriptor, Func <IMessage, IRichMessageDescriptor, ValueTask> asyncHandler) { Logger.LogInformation($"Subscribing: Exchange: {descriptor.MessageGroup}, Topic: {descriptor.MessageTopic}"); _subscribers.AddOrUpdate(descriptor, asyncHandler, (desc, oldValue) => asyncHandler); CreateConsumerChannel(descriptor); }
public void Subscribe(IMessageDescriptor descriptor, Func <IMessage, ValueTask> asyncHandler) => Subscribe(descriptor, async(msg, desc) => await asyncHandler(msg));