/// <summary>
        /// Provides the observer with new data. This method is invoked by an observable.
        /// </summary>
        /// <param name="topic">The topic.</param>
        /// <param name="message">The current notification information.</param>
        /// <param name="entityId">The entity id of the observable.</param>
        /// <param name="observers">A list of observers in the same cluster node. This field is optional.
        /// When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the list.</param>
        /// <returns>The asynchronous result of the operation.</returns>
        public async Task NotifyObserverAsync(string topic, Message message, EntityId entityId, IEnumerable <EntityId> observers)
        {
            EntityId id = null;

            for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++)
            {
                try
                {
                    id = await this.GetEntityIdAsync();

                    break;
                }
                catch (FabricTransientException ex)
                {
                    ServiceEventSource.Current.Error(ex);
                    if (k == ConfigurationHelper.MaxQueryRetryCount)
                    {
                        throw new TimeoutException(Constants.RetryTimeoutExhausted);
                    }
                }
                catch (AggregateException ex)
                {
                    foreach (Exception e in ex.InnerExceptions)
                    {
                        ServiceEventSource.Current.Error(e);
                    }
                    throw;
                }
                catch (Exception ex)
                {
                    ServiceEventSource.Current.Error(ex);
                    throw;
                }
                await Task.Delay(ConfigurationHelper.BackoffQueryDelay);
            }
            try
            {
                ServiceEventSource.Current.Message(
                    $"Message Received.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Message]: Topic=[{topic}] Body=[{message?.Body ?? "NULL"}]");
                if (observers != null)
                {
                    IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList();
                    if (observerList.Any())
                    {
                        StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}");
                        foreach (EntityId item in observerList)
                        {
                            builder.Append($"\r\n[To]: {item}");
                        }
                        ServiceEventSource.Current.Message(builder.ToString());
                        List <Task> taskList = new List <Task>();
                        taskList.AddRange(
                            observerList.Select(
                                observer => ProcessingHelper.NotifyObserverAsync(
                                    topic,
                                    message,
                                    observer,
                                    entityId,
                                    null)));
                        await Task.WhenAll(taskList.ToArray());
                    }
                }
            }
            catch (AggregateException ex)
            {
                foreach (Exception e in ex.InnerExceptions)
                {
                    ServiceEventSource.Current.Error(e);
                }
                throw;
            }
            catch (Exception ex)
            {
                ServiceEventSource.Current.Error(ex);
                throw;
            }
            if (this.NotificationMessageReceived == null)
            {
                return;
            }
            try
            {
                Delegate[] invocationList            = this.NotificationMessageReceived.GetInvocationList();
                Task[]     handlerTasks              = new Task[invocationList.Length];
                NotificationEventArgs <Message> args = new NotificationEventArgs <Message>(topic, message, entityId);
                for (int i = 0; i < invocationList.Length; i++)
                {
                    handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <NotificationEventArgs <Message>, Task>)invocationList[i], args);
                }
                await Task.WhenAll(handlerTasks);
            }
            catch (AggregateException ex)
            {
                foreach (Exception e in ex.InnerExceptions)
                {
                    ServiceEventSource.Current.Error(e);
                }
            }
            catch (Exception ex)
            {
                ServiceEventSource.Current.Error(ex);
            }
        }
        /// <summary>
        ///     Provides the observer with new data. This method is invoked by an observable.
        /// </summary>
        /// <param name="topic">The topic.</param>
        /// <param name="message">The current notification information.</param>
        /// <param name="entityId">The entity id of the observable.</param>
        /// <param name="observers">
        ///     A list of observers in the same cluster node. This field is optional.
        ///     When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the
        ///     list.
        /// </param>
        /// <returns>The asynchronous result of the operation.</returns>
        public virtual async Task NotifyObserverAsync(string topic, Message message, EntityId entityId, IEnumerable <EntityId> observers)
        {
            try
            {
                EntityId id = await this.GetEntityIdAsync();

                if (id == null)
                {
                    return;
                }
                if (entityId == null)
                {
                    entityId = new EntityId {
                        ActorId = ActorId.CreateRandom(), ServiceUri = this.ServiceUri
                    }
                }
                ;
                ActorEventSource.Current.Message(
                    $"Message Received.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Message]: Topic=[{topic}] Body=[{message?.Body ?? "NULL"}]");
                if (observers != null)
                {
                    IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList();
                    if (observerList.Any())
                    {
                        StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}");
                        foreach (EntityId item in observerList)
                        {
                            builder.Append($"\r\n[To]: {item}");
                        }
                        ActorEventSource.Current.Message(builder.ToString());
                        List <Task> taskList = new List <Task>();
                        taskList.AddRange(
                            observerList.Select(
                                observer => ProcessingHelper.NotifyObserverAsync(
                                    topic,
                                    message,
                                    observer,
                                    entityId,
                                    null)));
                        await Task.WhenAll(taskList.ToArray());
                    }
                }
            }
            catch (AggregateException ex)
            {
                foreach (Exception e in ex.InnerExceptions)
                {
                    ActorEventSource.Current.Error(e);
                }
                throw;
            }
            catch (Exception ex)
            {
                ActorEventSource.Current.Error(ex);
                throw;
            }
            if (this.NotificationMessageReceived == null)
            {
                return;
            }
            try
            {
                Delegate[] invocationList            = this.NotificationMessageReceived.GetInvocationList();
                Task[]     handlerTasks              = new Task[invocationList.Length];
                NotificationEventArgs <Message> args = new NotificationEventArgs <Message>(topic, message, entityId);
                for (int i = 0; i < invocationList.Length; i++)
                {
                    handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <NotificationEventArgs <Message>, Task>)invocationList[i], args);
                }
                await Task.WhenAll(handlerTasks);
            }
            catch (AggregateException ex)
            {
                foreach (Exception e in ex.InnerExceptions)
                {
                    ActorEventSource.Current.Error(e);
                }
            }
            catch (Exception ex)
            {
                ActorEventSource.Current.Error(ex);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Sends data to observers for a given topic.
        /// </summary>
        /// <param name="topic">The topic.</param>
        /// <param name="message">The current notification information.</param>
        /// <param name="useObserverAsProxy">Observable uses one observer for each cluster node as a proxy when true,
        /// it directly sends the message to all observers otherwise.</param>
        /// <returns>The asynchronous result of the operation.</returns>
        public async Task NotifyObserversAsync(string topic, Message message, bool useObserverAsProxy)
        {
            EntityId id = await this.GetEntityIdAsync();

            if (id == null)
            {
                return;
            }
            if (string.IsNullOrWhiteSpace(topic))
            {
                throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic));
            }
            ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic);

            if (!topicState.HasValue)
            {
                throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]");
            }
            Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value;

            if (string.IsNullOrWhiteSpace(message?.Body))
            {
                throw new ArgumentException($"The {nameof(message)} parameter cannot be null.", nameof(message));
            }
            try
            {
                List <Task> taskList = new List <Task>();
                if (useObserverAsProxy)
                {
                    if (JsonSerializerHelper.IsJson(message.Body))
                    {
                        // Create the list of observers: an observer is added to the list only at least of of its
                        // filter predicates is satisified by the message.
                        JObject jObject = JsonSerializerHelper.Deserialize(message.Body);
                        IEnumerable <EntityId> observerEnumerable = (from subscriptionInfo in observerDictionary.
                                                                     Where(kvp => kvp.Value.Predicates.Any()).
                                                                     Select(observer => observer.Value)
                                                                     let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject))
                                                                              where ok
                                                                              select subscriptionInfo.EntityId);

                        // observers are grouped by NodeName
                        taskList.AddRange(
                            observerEnumerable.
                            GroupBy(e => e.NodeName).
                            Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)).
                            Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2)));
                    }
                    else
                    {
                        // observers are grouped by NodeName
                        taskList.AddRange(
                            observerDictionary.
                            Select(kvp => kvp.Value.EntityId).
                            GroupBy(e => e.NodeName).
                            Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)).
                            Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2)));
                    }
                }
                else
                {
                    if (JsonSerializerHelper.IsJson(message.Body))
                    {
                        JObject jObject = JsonSerializerHelper.Deserialize(message.Body);
                        taskList.AddRange(
                            (from subscriptionInfo in observerDictionary.
                             Where(kvp => kvp.Value.Predicates.Any()).
                             Select(observer => observer.Value)
                             let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject))
                                      where ok
                                      select subscriptionInfo.EntityId).
                            Select(
                                entityId => ProcessingHelper.NotifyObserverAsync(
                                    topic,
                                    message,
                                    entityId,
                                    id,
                                    null)));
                    }
                    else
                    {
                        taskList.AddRange(
                            observerDictionary.Select(
                                observer => ProcessingHelper.NotifyObserverAsync(
                                    topic,
                                    message,
                                    observer.Value.EntityId,
                                    id,
                                    null)));
                    }
                }
                await Task.WhenAll(taskList.ToArray());
            }
            catch (AggregateException ex)
            {
                foreach (Exception e in ex.InnerExceptions)
                {
                    ActorEventSource.Current.Error(e);
                }
                throw;
            }
            catch (Exception ex)
            {
                ActorEventSource.Current.Error(ex);
                throw;
            }
        }