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);
                    }
                }
            }));
        }
        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);
                }
            });
        }