public static void ScanToRegisterWorker()
        {
            var allChannel = RedisServices.HashGetAll(_keyListChannelName);

            foreach (var channel in allChannel)
            {
                RegisterNotifyWorkerAndStart(channel.Key);
            }
        }
        public static List <SubscriberInfo> GetSubscribers(string channelName)
        {
            string channelSubscriber = GetKeySubscribersForChannel(channelName);
            var    subscribers       = RedisServices.HashGetAll(channelSubscriber);

            return(subscribers.Select(i => new SubscriberInfo {
                Name = i.Key
            }).ToList());
        }
        private static string GetProcessTypeOfChannel(string type)
        {
            var processTypeOfChannel = RedisServices.HashGet(_keyListChannelName, type);

            if (string.IsNullOrEmpty(processTypeOfChannel))
            {
                processTypeOfChannel = ProcessType.Topic.ToString();
            }
            return(processTypeOfChannel);
        }
        public static void Publish <T>(T data, ProcessType processType = ProcessType.Topic)
        {
            var type = data.GetType().AssemblyQualifiedName;

            RedisServices.HashSet(_keyListChannelName, new KeyValuePair <string, string>(type, processType.ToString()));

            string queueDataName = GetKeyQueueDataForChannel(type);

            RedisServices.TryEnqueue(queueDataName, JsonConvert.SerializeObject(data));
        }
        public static void Unsubscribe(string channel, string subscriberName)
        {
            string processTypeOfChannel           = GetProcessTypeOfChannel(channel);
            string queueDataNameChannelSubscriber = GetKeyToRealPublishFromChannelToSubscriber(subscriberName, channel, processTypeOfChannel);

            RedisServices.UnSubscribe(queueDataNameChannelSubscriber);

            string channelSubscriber = GetKeySubscribersForChannel(channel);

            RedisServices.HashDelete(channelSubscriber, subscriberName);
        }
        public static ChannelInfo GetChannelInfo(string channelName)
        {
            var channelVal = RedisServices.HashGet(_keyListChannelName, channelName);

            return(new ChannelInfo()
            {
                DataStructure = channelVal,
                Name = channelName,
                Subscribers = GetSubscribers(channelName),
                PendingDataQueueLength = GetPendingDataQueueLength(channelName),
                SuccessDataQueueLength = GetSuccessDataQueueLength(channelName),
                ErrorDataQueueLength = GetErrorDataQueueLength(channelName)
            });
        }
        public static List <ChannelInfo> GetAllChannelInfo()
        {
            var allChannel = RedisServices.HashGetAll(_keyListChannelName);

            return(allChannel.Select(d => new ChannelInfo()
            {
                DataStructure = d.Value,
                Name = d.Key,
                Subscribers = GetSubscribers(d.Key),
                PendingDataQueueLength = GetPendingDataQueueLength(d.Value),
                SuccessDataQueueLength = GetSuccessDataQueueLength(d.Value),
                ErrorDataQueueLength = GetErrorDataQueueLength(d.Value)
            }).ToList());
        }
        public static void Dispose()
        {
            var allChannel = RedisServices.HashGetAll(_keyListChannelName);

            foreach (var c in allChannel)
            {
                string channelSubscriber = GetKeySubscribersForChannel(c.Key);

                var subscribers = RedisServices.HashGetAll(channelSubscriber);

                foreach (var sub in subscribers)
                {
                    Unsubscribe(c.Key, sub.Key);
                }
            }
        }
        private static Thread CreateNotifyWorker(string type)
        {
            return(new Thread(() =>
            {
                while (true)
                {
                    try
                    {
                        string queueDataName = GetKeyQueueDataForChannel(type);

                        var processTypeOfChannel = RedisServices.HashGet(_keyListChannelName, type);
                        string channelSubscriber = GetKeySubscribersForChannel(type);

                        var subscribers = RedisServices.HashGetAll(channelSubscriber);

                        if (RedisServices.QueueHasValue(queueDataName))
                        {
                            ProcessType processType = GetProcessTypeByName(processTypeOfChannel);
                            if (processType == ProcessType.Topic)
                            {
                                string queueDataNameChannelSubscriber = GetKeyToRealPublishFromChannelToSubscriber(string.Empty, type, processTypeOfChannel);
                                //notify to parent, then parent will find subscribers to process same data
                                RedisServices.Publish(queueDataNameChannelSubscriber, processTypeOfChannel);
                            }
                            else
                            {
                                foreach (var subc in subscribers)
                                {
                                    string queueDataNameChannelSubscriber = GetKeyToRealPublishFromChannelToSubscriber(subc.Key, type, processTypeOfChannel);

                                    RedisServices.Publish(queueDataNameChannelSubscriber, processTypeOfChannel);
                                    //notify to subscribers to dequeue data and process
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                    }
                    finally
                    {
                        Thread.Sleep(100);
                    }
                }
            }));
        }
        /// <summary>
        /// type = channel
        /// </summary>
        /// <param name="type"></param>
        static void RegisterNotifyWorkerAndStart(string type)
        {
            var processTypeOfChannel = RedisServices.HashGet(_keyListChannelName, type);

            var channelRegistered = type + "_" + processTypeOfChannel;

            lock (_mapedHandleCounter)
            {
                int count;
                if (_mapedHandleCounter.TryGetValue(channelRegistered, out count))
                {
                    if (count > 0)
                    {
                        _mapedHandleCounter[channelRegistered] = 1;
                        // allow only one worker (thread dequeue data)
                        return;
                    }
                    ////if allow more than one thread to dequeue
                    //List<Thread> listWorker;

                    //if (_mapedHandleWorkers.TryGetValue(type, out listWorker))
                    //{
                    //    var nextWorker = CreateDequeuWorker(type);
                    //    listWorker.Add(nextWorker);
                    //    nextWorker.Start();
                    //    _mapedHandleWorkers[type] = listWorker;
                    //    _mapedHandleCounter[type] = count + 1;
                    //}
                }
                else
                {
                    _mapedHandleCounter[channelRegistered] = 1;

                    var firstWorker = CreateNotifyWorker(type);

                    _mapedHandleWorkers[channelRegistered] = new List <Thread>()
                    {
                        firstWorker
                    };
                    //make sure alway have at less one worker to dequeue
                    firstWorker.Start();
                }
            }
        }
        static void TryDoJob(string typeFullAssemblyQualifiedName, string data, Action <object> handle)
        {
            var type      = typeFullAssemblyQualifiedName;
            var queueName = GetKeyQueueDataForChannel(type);

            try
            {
                var jobj       = JsonConvert.DeserializeObject(data) as Newtonsoft.Json.Linq.JObject;
                var objectType = Type.GetType(type, false, true);

                var o = jobj.ToObject(objectType);

                handle(o);

                string successQueueDataName = queueName + "_Success";
                RedisServices.TryEnqueue(successQueueDataName, data);
                Thread.Sleep(1);
            }
            catch (Exception ex)
            {
                string errorQueueDataName = queueName + "_Error";
                RedisServices.TryEnqueue(errorQueueDataName, data);
            }
        }
        public static long GetSuccessDataQueueLength(string channelName)
        {
            var queueName = GetKeyQueueDataForChannel(channelName) + "_Success";

            return(RedisServices.QueueLength(queueName));
        }
        public static void Subscribe(string subscriberName, string typeFullAssemblyQualifiedName, Action <object> handle)
        {
            var type = typeFullAssemblyQualifiedName;

            string channelSubscriber = GetKeySubscribersForChannel(type);

            if (RedisServices.HashExisted(channelSubscriber, subscriberName))
            {
                //todo: should unsubscribe before subscribe
                //throw new MessageBussServices.ExistedSubscriber($"Existed subscriber {subscriberName} in channel {channelSubscriber}");
            }

            RedisServices.HashSet(channelSubscriber, new KeyValuePair <string, string>(subscriberName, subscriberName));

            string channelPubSubChild = GetKeyToRealPublishFromChannelToSubscriber(subscriberName, type, ProcessType.Topic.ToString());

            //regist to process data for data structure pubsub
            RedisServices.Subscribe(channelPubSubChild, (data) =>
            {
                TryDoJob(typeFullAssemblyQualifiedName, data, handle);
            });

            string channelPubSubParent = GetKeyToRealPublishFromChannelToSubscriber(string.Empty, type, ProcessType.Topic.ToString());

            //regist to process if pubsub try dequeue get data and publish to subscriber
            RedisServices.Subscribe(channelPubSubParent, (msg) =>
            {
                string data;
                string queueDataName = GetKeyQueueDataForChannel(type);
                while (RedisServices.TryDequeue(queueDataName, out data))
                {
                    var subscribers = RedisServices.HashGetAll(channelSubscriber);

                    foreach (var subc in subscribers)
                    {
                        string queueDataNameChannelSubscriber = GetKeyToRealPublishFromChannelToSubscriber(subc.Key, type, ProcessType.Topic.ToString());
                        //channelPubSubChild
                        RedisServices.Publish(queueDataNameChannelSubscriber, data);
                    }
                }
            });

            string channelQueue = GetKeyToRealPublishFromChannelToSubscriber(subscriberName, type, ProcessType.Queue.ToString());

            //regist to process if data structure is queue
            RedisServices.Subscribe(channelQueue, (msg) =>
            {
                string data;
                string queueDataName = GetKeyQueueDataForChannel(type);
                while (RedisServices.TryDequeue(queueDataName, out data))
                {
                    TryDoJob(type, data, handle);
                }
            });

            string channelStack = GetKeyToRealPublishFromChannelToSubscriber(subscriberName, type, ProcessType.Stack.ToString());

            //regist to process if data structure is stack
            RedisServices.Subscribe(channelStack, (msg) =>
            {
                string data;
                string queueDataName = GetKeyQueueDataForChannel(type);
                while (RedisServices.TryPop(queueDataName, out data))
                {
                    TryDoJob(type, data, handle);
                }
            });
        }