/// <summary> /// /// <para>CustomSubscribe:</para> /// /// <para>Subscribes to given custom topic</para> /// /// <para>Check <seealso cref="IBPubSubServiceInterface.CustomSubscribe"/> for detailed documentation</para> /// /// </summary> public bool CustomSubscribe( string _CustomTopic, Action <string, string> _OnMessage, Action <string> _ErrorMessageAction = null, bool _SubscribeSingleMessage = false) { if (_CustomTopic != null && _CustomTopic.Length > 0 && _OnMessage != null) { FailoverCheck(); try { RedisConnection.GetSubscriber().Subscribe( _CustomTopic, (RedisChannel Channel, RedisValue Value) => { if (UniqueMessageDeliveryEnsurer != null) { var Message = Value.ToString(); UniqueMessageDeliveryEnsurer.Subscribe_ClearAndExtractTimestampFromMessage(ref Message, out string TimestampHash); if (UniqueMessageDeliveryEnsurer.Subscription_EnsureUniqueDelivery(Channel, TimestampHash, _ErrorMessageAction)) { _OnMessage?.Invoke(Channel, Message); } } else { _OnMessage?.Invoke(Channel, Value.ToString()); } });
/// <summary> /// /// <para>CustomSubscribe:</para> /// /// <para>Subscribes to given custom topic</para> /// /// <para>Check <seealso cref="IBPubSubServiceInterface.CustomSubscribe"/> for detailed documentation</para> /// /// </summary> public bool CustomSubscribe(string _CustomTopic, Action <string, string> _OnMessage, Action <string> _ErrorMessageAction = null, bool _SubscribeSingleMessage = false) { if (_CustomTopic != null && _CustomTopic.Length > 0 && _OnMessage != null && BUtility.CalculateStringMD5(_CustomTopic, out string TopicMD5, _ErrorMessageAction)) { if (EnsureQueueExists(TopicMD5, out string QueueUrl, _ErrorMessageAction)) { var SubscriptionCancellationVar = new BValue <bool>(false, EBProducerStatus.MultipleProducer); var SubscriptionThread = new Thread(() => { Thread.CurrentThread.IsBackground = true; while (!SubscriptionCancellationVar.Get()) { ReceiveMessageResponse Response; try { using (var ReceiveMessageTask = SQSClient.ReceiveMessageAsync(QueueUrl)) { ReceiveMessageTask.Wait(); Response = ReceiveMessageTask.Result; } } catch (Exception e) { Response = null; _ErrorMessageAction?.Invoke("BPubSubServiceAWS->CustomSubscribe: " + e.Message + ", Trace: " + e.StackTrace); if (e.InnerException != null && e.InnerException != e) { _ErrorMessageAction?.Invoke("BPubSubServiceAWS->CustomSubscribe->Inner: " + e.InnerException.Message + ", Trace: " + e.InnerException.StackTrace); } } if (Response == null || Response.Messages == null || Response.Messages.Count == 0) { Thread.Sleep(1000); continue; } var AckDictionary = new Dictionary <string, string>(); foreach (var MessageContainer in Response.Messages) { if (MessageContainer != null) { if (!AckDictionary.ContainsKey(MessageContainer.MessageId)) { AckDictionary.Add(MessageContainer.MessageId, MessageContainer.ReceiptHandle); } string Data = MessageContainer.Body; if (UniqueMessageDeliveryEnsurer != null) { UniqueMessageDeliveryEnsurer.Subscribe_ClearAndExtractTimestampFromMessage(ref Data, out string TimestampHash); if (UniqueMessageDeliveryEnsurer.Subscription_EnsureUniqueDelivery(_CustomTopic, TimestampHash, _ErrorMessageAction)) { _OnMessage?.Invoke(_CustomTopic, Data); } } else { _OnMessage?.Invoke(_CustomTopic, Data); } } } var AckArray = new List <DeleteMessageBatchRequestEntry>(); foreach (var Current in AckDictionary) { AckArray.Add(new DeleteMessageBatchRequestEntry(Current.Key, Current.Value)); } try { using (var DeleteMessageBatchTask = SQSClient.DeleteMessageBatchAsync(QueueUrl, AckArray)) { DeleteMessageBatchTask.Wait(); } } catch (Exception e) { _ErrorMessageAction?.Invoke("BPubSubServiceAWS->CustomSubscribe: " + e.Message + ", Trace: " + e.StackTrace); if (e.InnerException != null && e.InnerException != e) { _ErrorMessageAction?.Invoke("BPubSubServiceAWS->CustomSubscribe->Inner: " + e.InnerException.Message + ", Trace: " + e.InnerException.StackTrace); } } } }); SubscriptionThread.Start(); lock (SubscriberThreadsDictionaryLock) { SubscriberThreadsDictionary.Add(_CustomTopic, new BTuple <Thread, BValue <bool> >(SubscriptionThread, SubscriptionCancellationVar)); } return(true); } } return(false); }
/// <summary> /// /// <para>CustomSubscribe:</para> /// /// <para>Subscribes to given custom topic</para> /// /// <para>Check <seealso cref="IBPubSubServiceInterface.CustomSubscribe"/> for detailed documentation</para> /// /// </summary> public bool CustomSubscribe( string _CustomTopic, Action <string, string> _OnMessage, Action <string> _ErrorMessageAction = null, bool _SubscribeSingleMessage = false) { if (_CustomTopic != null && _CustomTopic.Length > 0 && _OnMessage != null) { _CustomTopic = GetGoogleFriendlyTopicName(_CustomTopic); if (GetSubscriber(out SubscriberServiceApiClient APIClientVar, out SubscriptionName SubscriptionNameVar, _CustomTopic, _ErrorMessageAction)) { var SubscriptionCancellationVar = new BValue <bool>(false, EBProducerStatus.MultipleProducer); var SubscriptionThread = new Thread(() => { Thread.CurrentThread.IsBackground = true; while (!SubscriptionCancellationVar.Get()) { PullResponse Response = null; try { Response = APIClientVar.Pull(SubscriptionNameVar, true, 1000); } catch (Exception e) { if (e is RpcException && (e as RpcException).StatusCode == StatusCode.DeadlineExceeded) { Thread.Sleep(1000); continue; } Response = null; _ErrorMessageAction?.Invoke("BPubSubServiceGC->CustomSubscribe: " + e.Message + ", Trace: " + e.StackTrace); if (e.InnerException != null && e.InnerException != e) { _ErrorMessageAction?.Invoke("BPubSubServiceGC->CustomSubscribe->Inner: " + e.InnerException.Message + ", Trace: " + e.InnerException.StackTrace); } } if (Response == null || Response.ReceivedMessages == null || !Response.ReceivedMessages.Any()) { Thread.Sleep(1000); continue; } var MessageContainers = Response.ReceivedMessages.ToArray(); if (MessageContainers != null && MessageContainers.Length > 0) { var AckArray = new List <string>(); foreach (var MessageContainer in MessageContainers) { if (MessageContainer != null) { if (!AckArray.Contains(MessageContainer.AckId)) { AckArray.Add(MessageContainer.AckId); } string Topic = GetTopicNameFromGoogleFriendlyName(_CustomTopic); string Data = MessageContainer.Message.Data.ToStringUtf8(); if (UniqueMessageDeliveryEnsurer != null) { UniqueMessageDeliveryEnsurer.Subscribe_ClearAndExtractTimestampFromMessage(ref Data, out string TimestampHash); if (UniqueMessageDeliveryEnsurer.Subscription_EnsureUniqueDelivery(Topic, TimestampHash, _ErrorMessageAction)) { _OnMessage?.Invoke(Topic, Data); } } else { _OnMessage?.Invoke(Topic, Data); } } } try { APIClientVar.Acknowledge(SubscriptionNameVar, AckArray); } catch (Exception e) { if (e is RpcException && (e as RpcException).Status.StatusCode == StatusCode.InvalidArgument) { //That is fine, probably due to previous subscriptions } else { _ErrorMessageAction?.Invoke("BPubSubServiceGC->CustomSubscribe: " + e.Message + ", Trace: " + e.StackTrace); if (e.InnerException != null && e.InnerException != e) { _ErrorMessageAction?.Invoke("BPubSubServiceGC->CustomSubscribe->Inner: " + e.InnerException.Message + ", Trace: " + e.InnerException.StackTrace); } } } } } }); SubscriptionThread.Start(); lock (SubscriberThreadsDictionaryLock) { SubscriberThreadsDictionary.Add(SubscriptionNameVar, new BTuple <Thread, BValue <bool> >(SubscriptionThread, SubscriptionCancellationVar)); } return(true); } } return(false); }
public bool CustomSubscribe(string _CustomTopic, Action <string, string> _OnMessage, Action <string> _ErrorMessageAction = null, bool _SubscribeSingleMessage = false) { if (_CustomTopic != null && _CustomTopic.Length > 0 && _OnMessage != null) { _CustomTopic = GetAzureFriendlyTopicName(_CustomTopic); if (EnsureTopicExists(_CustomTopic, out ITopicClient _TopicClient, _ErrorMessageAction)) { if (EnsureSubscriptionExists(_CustomTopic, _TopicClient, out Microsoft.Azure.ServiceBus.ISubscriptionClient _SubscriptionClient, _ErrorMessageAction)) { var SubscriptionCancellationVar = new BValue <bool>(false, EBProducerStatus.MultipleProducer); var SubscriptionThread = new Thread(() => { Thread.CurrentThread.IsBackground = true; try { // Define exception receiver handler Func <ExceptionReceivedEventArgs, Task> ExceptionReceiverHandler = (ExceptionReceivedEventArgs _EventArgs) => { _ErrorMessageAction?.Invoke($"BPubSubServiceAzure->CustomSubscribe->ExceptionReceiverHandler: {_EventArgs.Exception.Message}, Action: {_EventArgs.ExceptionReceivedContext.Action}, EntityPath: {_EventArgs.ExceptionReceivedContext.EntityPath}, Endpoint: {_EventArgs.ExceptionReceivedContext.Endpoint}, ClientId: {_EventArgs.ExceptionReceivedContext.ClientId}, Trace: {_EventArgs.Exception.StackTrace}"); RemoveSubscriberThread(_CustomTopic); if (!_SubscriptionClient.IsClosedOrClosing) { using (var CloseSubscriptionTask = _SubscriptionClient.CloseAsync()) { CloseSubscriptionTask.Wait(); } } return(Task.CompletedTask); }; // Configure the message handler options in terms of exception handling, number of concurrent messages to deliver, etc. var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceiverHandler) { // Maximum number of concurrent calls to the callback ProcessMessagesAsync(), set to 1 for simplicity. // Set it according to how many messages the application wants to process in parallel. MaxConcurrentCalls = 1, // Indicates whether the message pump should automatically complete the messages after returning from user callback. // False below indicates the complete operation is handled by the user callback as in ProcessMessagesAsync(). AutoComplete = false }; // Register the function that processes messages. _SubscriptionClient.RegisterMessageHandler(async(Message MessageContainer, CancellationToken token) => { if (MessageContainer != null) { string Topic = GetTopicNameFromAzureFriendlyName(_CustomTopic); string Data = Encoding.UTF8.GetString(MessageContainer.Body); if (MessageContainer.Label != null && MessageContainer.Label.Equals(_CustomTopic)) { _ErrorMessageAction?.Invoke("BPubSubServiceAzure->CustomSubscribe: Received [Internal] pub/sub message."); } else { _ErrorMessageAction?.Invoke("BPubSubServiceAzure->CustomSubscribe: Received [StorageAccount] pub/sub message."); } if (UniqueMessageDeliveryEnsurer != null) { UniqueMessageDeliveryEnsurer.Subscribe_ClearAndExtractTimestampFromMessage(ref Data, out string TimestampHash); if (UniqueMessageDeliveryEnsurer.Subscription_EnsureUniqueDelivery(Topic, TimestampHash, _ErrorMessageAction)) { _OnMessage?.Invoke(Topic, Data); RemoveSubscriberThread(_CustomTopic); if (!_SubscriptionClient.IsClosedOrClosing) { await _SubscriptionClient.CloseAsync(); } } } else { _OnMessage?.Invoke(Topic, Data); } await _SubscriptionClient.CompleteAsync(MessageContainer.SystemProperties.LockToken); if (_SubscribeSingleMessage) { RemoveSubscriberThread(_CustomTopic); if (!_SubscriptionClient.IsClosedOrClosing) { await _SubscriptionClient.CloseAsync(); } } } }, messageHandlerOptions); } catch (Exception e) { _ErrorMessageAction?.Invoke("BPubSubServiceAzure->CustomSubscribe: " + e.Message + ", Trace: " + e.StackTrace); if (e.InnerException != null && e.InnerException != e) { _ErrorMessageAction?.Invoke("BPubSubServiceAzure->CustomSubscribe->InnerException: " + e.InnerException.Message + ", Trace: " + e.InnerException.StackTrace); } RemoveSubscriberThread(_CustomTopic); if (!_SubscriptionClient.IsClosedOrClosing) { using (var CloseSubscriptionTask = _SubscriptionClient.CloseAsync()) { CloseSubscriptionTask.Wait(); } } } while (!SubscriptionCancellationVar.Get()) { //Wait until delete Thread.Sleep(500); } if (!_SubscriptionClient.IsClosedOrClosing) { using (var CloseSubscriptionTask = _SubscriptionClient.CloseAsync()) { CloseSubscriptionTask.Wait(); } } }); SubscriptionThread.Start(); lock (SubscriberThreadsDictionaryLock) { SubscriberThreadsDictionary.Add(_CustomTopic, new BTuple <Thread, BValue <bool> >(SubscriptionThread, SubscriptionCancellationVar)); } return(true); } } } return(false); }