private FixedSizedQueue <ProcessedOn> GetProcessedOnQueue(EventData eventObject)
        {
            // for SCOM below 2019(10.x.x.x), Rule Rate limit has bug, we use single ProcessOnQueue for all rules
            var key = "Common";

            if (Version.Major >= 10)
            {
                key = $"{eventObject.AlarmData.EventCategory}_{eventObject.EventId}"; // group by rule
            }

            bool exists = ProcessedOnQueueMap.ContainsKey(key);

            if (!exists)
            {
                FixedSizedQueue <ProcessedOn> ProcessedOnQueue = new FixedSizedQueue <ProcessedOn>(RateLimitQueueSize);
                ProcessedOnQueueMap.Add(key, ProcessedOnQueue);
            }

            return(ProcessedOnQueueMap[key]);
        }
        /// <summary>
        /// 处理告警队列
        /// </summary>
        private void StartAlarmEventProcessor()
        {
            Version = MGroup.Instance.Version;
            logger.Polling.Info("Current Management Server version is: " + Version.ToString());

            RateLimitQueueSize = this.pluginConfig.RateLimitQueueSize;
            RateLimitTimeSpan  = TimeSpan.FromSeconds(this.pluginConfig.RateLimitTimeSpan);

            if (AlarmProcessor == null)
            {
                AlarmProcessor = new Thread(delegate()
                {
                    while (this.IsRunning) // TODO(turnbig) 假如Queue里面还有未处理的数据,直接丢弃?
                    {
                        logger.Polling.Info($"Current Alarm Processing Queue amount: {AlarmQueue.Count}.");

                        if (AlarmQueue.Count > 0 || ReceiveAlarmEvent.WaitOne())
                        {
                            AlarmData alarm = null;
                            lock (this.locker)
                            {
                                if (AlarmQueue.Count > 0)
                                {
                                    alarm = AlarmQueue.Dequeue();
                                }
                            }

                            if (alarm != null)
                            {
                                EventData eventObject = new EventData(alarm, this.FusionDirectorIp);
                                logger.Polling.Info($"[{alarm.Sn}] Start processing alarm:: Source:{alarm.DeviceId}, Category:{alarm.EventCategory}, Status: {alarm.Status}.");

                                var objectId                            = eventObject.UnionId;
                                string mpClazzName                      = EventCategory.BMC.Equals(alarm.EventCategory) ? EntityTypeConst.Server.MainName : EntityTypeConst.Enclosure.MainName;
                                ManagementPackClass mpClazz             = MGroup.Instance.GetManagementPackClass(mpClazzName);
                                MonitoringDeviceObject monitoringObject = BaseConnector.GetDeviceByObjectId(mpClazz, objectId);
                                if (monitoringObject == null)
                                {
                                    // TODO(turnbig.net) should we trigger an update server task, and retry later?
                                    logger.Polling.Warn($"[{alarm.Sn}] No MonitoringObject({objectId}) exists, alarm will be ignored.");
                                    continue;
                                }

                                // waiting for monitoring-object ready.
                                WaitForDeviceMonitored(monitoringObject);


                                var now = DateTime.Now;
                                ProcessedOn previewNProcessedOn = null;
                                FixedSizedQueue <ProcessedOn> processedOnQueue = GetProcessedOnQueue(eventObject);
                                if (EventStatus.Cleared.Equals(alarm.Status))
                                {
                                    // Close SCOM alert
                                    CloseSCOMAlert(eventObject, monitoringObject);
                                }
                                else if (ShouldProcessedAlarmLevels.Contains(eventObject.LevelId))
                                {
                                    // Create New EventLog for new alarms, and generate SCOM alert through associated rule
                                    CreateNewEventLogForAlarm(eventObject);
                                    // use a seperated process on tracker
                                    previewNProcessedOn = processedOnQueue.Enqueue(new ProcessedOn(now));
                                }

                                if (previewNProcessedOn != null)
                                {
                                    TimeSpan timeSpan = now - previewNProcessedOn.Timestamp;
                                    // do not know why system time was changed to yestoday.
                                    if (now >= previewNProcessedOn.Timestamp && timeSpan < RateLimitTimeSpan)
                                    {
                                        TimeSpan timeout = RateLimitTimeSpan - timeSpan;
                                        logger.Polling.Info($"Alarm processing reach rate limit, {processedOnQueue.Size} alarms have been processed during time span {timeSpan}, will sleep {timeout} now.");
                                        Thread.Sleep(timeout);
                                    }
                                }
                            }
                        }
                    }
                });
            }

            this.AlarmProcessor.Start();
            logger.Polling.Info("Alarm processor starts successfully.");
        }