/// <summary>
        /// 添加ACK 信息,供CONSUMER使用
        /// </summary>
        /// <param name="consumerUri"> </param>
        /// <param name="server"></param>
        /// <param name="ack"></param>
        public void AppendAck(string consumerUri,PhysicalServer server,ConsumerAck ack)
        {
            Guard.ArgumentNotNull(server, "server");
            Guard.ArgumentNotNull(ack, "ack");

            Output.AddOrUpdate(server.ServerDomainName,
                               s =>
                                   {
                                       var queue = new ConcurrentQueue<CBox<Tuple<string, ConsumerAck>>>();
                                       queue.Enqueue(new CBox<Tuple<string, ConsumerAck>> { Value = new Tuple<string, ConsumerAck>(consumerUri, ack) });
                                       //定时Ack
                                       _timerScheduler.Register(() =>
                                                                    {
                                                                        var tmp = server;
                                                                        RunTaskAck(tmp);
                                                                    }, s, _ackIntervalTime, false);
                                           //对于新增的SERVER 启动TASK 进行自动ACK
                                       return queue;
                                   },
                               (s, d) =>
                                   {
                                       var tmp = server;
                                       d.Enqueue(new CBox<Tuple<string, ConsumerAck>>
                                                     {Value = new Tuple<string, ConsumerAck>(consumerUri, ack)});

                                       if (d.Count >= Consts.Consumer_DefaultAckQueueBoundary) //Queue 数量大于
                                       {
                                           System.Threading.ThreadPool.QueueUserWorkItem(RunTaskAck, tmp);
                                       }
                                       return d;
                                   });
        }
        public PhysicalServer Schedule(string consumer, int receiveTimeout,bool sync)
        {
            const string title = "noserver";
            if (string.IsNullOrEmpty(consumer))
            {
                Logger.Write("consumer is null", LogLevel.Warn, title);
                return null;
            }

            WeightedRandomScheduling<PhysicalServer> wrs;
            if (!_consumerWrsList.TryGetValue(consumer, out wrs))
            {
                Logger.Write("consumer is not exists int WRS LIST", LogLevel.Warn, title, new[] { new KeyValue { Key = "consumer", Value = consumer } });
                return null;
            }

            var server = wrs.Schedule();
            if (server == null)
            {
                Logger.Write("no server when server run schedule", LogLevel.Warn, title, new[] { new KeyValue { Key = "consumer", Value = consumer } });
                return null;
            }
            //if (!server.Mark) return server; //Mark标记为false的不作退避
            string serverName = "";
            DateTime date = DateTime.MaxValue;
            lock (_lockObject)
            {
                Dictionary<string, Tuple<int, int, DateTime>> dictionary;
                if (!_noMessageList.TryGetValue(consumer, out dictionary)) return server;//此CONSUMER没有无消息的SERVER,直接返回
                if (!dictionary.ContainsKey(server.ServerName)) return server; //CONSUMER对应此SERVER, 不存在列表中,直接返回
                if ((DateTime.Now - dictionary[server.ServerName].Item3).TotalMilliseconds > receiveTimeout / 2) return server;//如超500ms,则继续请求

                var tmp = dictionary.ToList();
                if (tmp.Count >= wrs.Servers.Length)
                {
                    PhysicalServer[] servers = new PhysicalServer[wrs.Servers.Length];
                    wrs.Servers.CopyTo(servers, 0);
                    //当都没有数据时,取最长时间未取数据的SERVER
                    foreach (var item in tmp)
                    {
                        var s = servers.FirstOrDefault(x => x.ServerName == item.Key);//如在server中不存在的,不作处理
                        if (s == null) continue;
                        if ( date > item.Value.Item3)
                        {
                            date = item.Value.Item3;
                            serverName = item.Key;
                            server = s;
                        }
                    }
                }
            }
            if (!string.IsNullOrEmpty(serverName))
            {
                if (!sync) Block(consumer, date, receiveTimeout);//异步做休眠
                return server;
            }
            return Schedule(consumer,receiveTimeout,sync);
        }
 private void RemoveServer(string consumerUri, PhysicalServer server)
 {
     if (server == null) return;
     //移除无数据SERVER
     Dictionary<string, Tuple<int, int, DateTime>> dictionary;
     if (_noMessageList.TryGetValue(consumerUri, out dictionary))
     {
         lock (_lockObject)
         {
             if (dictionary.ContainsKey(server.ServerName))
             {
                 dictionary.Remove(server.ServerName);
             }
         }
     }
 }
 //PULLING请求结束时调用
 public void EndPulling(string consumerUri, PhysicalServer server, bool hasMessages)
 {
     if (server == null) return;
     if (hasMessages)
     {
         RemoveServer(consumerUri, server);//有数据从无数据列表中迁除该SERVER
     }
     else
     {
         AddOrUpdateServer(consumerUri, server.ServerName);
         if (server.Mark == false)
             RemoveMarkServer(consumerUri, server.ServerName);
     }
 }
        /// <summary>
        /// 向服务端ACK
        /// </summary>
        /// <param name="server"></param>
        /// <param name="timeout"></param>
        /// <param name="chunk"></param>
        /// <returns></returns>
        public ChunkAck Ack(PhysicalServer server, int timeout, ConsumerAckChunk chunk)
        {
            Guard.ArgumentNotNull(server, "server");
            Guard.ArgumentNotNull(chunk, "chunk");
            Guard.ArgumentNotNullOrEmpty(server.ServerDomainName, "server.ServerDomainName");
            Guard.ArgumentNotNullOrEmpty(server.ServerName, "server.ServerName");

            MetricUtil.Set(new AckRequestCountMetric { ServerHostName = server.ServerName });
            foreach (var ack in chunk.ConsumerAcks)
            {
                MetricUtil.Set(new AckMessageCountMetric { Consumer = ack.Uri });
            }

            ChunkAck chunkAck=null;
            var watch = new Stopwatch();
            DispatcherServiceWrapper.Client client=null;
            try
            {
                client = Client.CreateDispatcherServiceClient(server.ServerDomainName, timeout);
                watch.Start();
                chunkAck = client.Ack(chunk);

                return chunkAck;
            }
            catch {
                if (client != null) client.Dispose();
                throw;
            }
            finally
            {
                watch.Stop();
                var milliseconds = watch.ElapsedMilliseconds;
                MetricUtil.Set(new AckResponseCountMetric { ServerHostName = server.ServerName, Latency = milliseconds, StatusCode = chunkAck == null ? "0" : chunkAck.StatusCode.ToString() });
                MetricUtil.Set(new AckResponseLatencyMetric { ServerHostName = server.ServerName }, milliseconds);
            }
        }
        /// <summary>
        /// 向服务端拉数据
        /// </summary>
        /// <param name="server"></param>
        /// <param name="receiveTimeout"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public SubChunk Pulling(PhysicalServer server, int receiveTimeout, PullingRequest request)
        {
            Guard.ArgumentNotNull(server, "server");
            Guard.ArgumentNotNull(request, "request");
            Guard.ArgumentNotNullOrEmpty(request.Uri, "request.Uri");
            Guard.ArgumentNotNullOrEmpty(server.ServerDomainName, "server.ServerDomainName");
            Guard.ArgumentNotNullOrEmpty(server.ServerName, "server.ServerName");
            SubChunk chunk= null;
            bool isNotMessage = true;
            long milliseconds=0;
            string errortype = "";
            
            MetricUtil.Set(new PullingRequestCountMetric
            {
                ServerHostName = server.ServerName,
                Consumer = request.Uri
            });
            var statuscode = 0;
            var watch = new Stopwatch();
            DispatcherServiceWrapper.Client client = null;
#if DEBUG
            var index = countor.AtomicIncrementAndGet();
#endif
            try
            {
#if DEBUG
                debugLog.Write(string.Format("Start servicepulling->{0} server->{1}", index, server.ServerName), request.Uri);
#endif
                client = Client.CreateDispatcherServiceClient(server.ServerDomainName, receiveTimeout);
            
                watch.Start();
                chunk = client.Pulling(request);
                isNotMessage = (chunk == null || chunk.Messages == null || chunk.Messages.Count < 1);
                statuscode = chunk == null ? 0 : (int)chunk.StatusCode;
            }
            catch (WebException webException)
            {
                if (client != null) client.Dispose();
                statuscode = (int)Convert(webException.Status);
                errortype = "webexception";
                if (webException.Message.IndexOf("timed out", StringComparison.InvariantCultureIgnoreCase) > 0)
                {
                    errortype = "timeout";
                }
                throw webException;
            }
            catch (TTransportException te)
            {
                if (client != null) client.Dispose();
                errortype = "ttransportexception";
                
                if ((te.Message.IndexOf("timed out", StringComparison.InvariantCultureIgnoreCase) > 0)||
                    (te.InnerException != null 
                    && (te.InnerException.Message.IndexOf("timed out", StringComparison.InvariantCultureIgnoreCase) > 0)))
                {
                    errortype = "timeout";
                }

                if (te.InnerException != null && te.InnerException is WebException)
                {
                    var webex = (WebException)te.InnerException;
                    statuscode = (int)Convert(webex.Status);
                }
                else
                {
                    statuscode = (int)Convert(te.Type);
                }
                throw te;
            }
            catch (Exception ex)
            {
                if (client != null) client.Dispose();
                errortype = "systemexception";
                throw ex;
            }
            finally
            {
                watch.Stop();
                milliseconds = watch.ElapsedMilliseconds;
                if(!isNotMessage && chunk !=null && chunk.Messages !=null)
                {
                    var messageSize = 0;
                    var messageCount = chunk.Messages.Count;
                    chunk.Messages.Iterate(x => messageSize += x.Pub != null ? x.Pub.Size : 0);
                    MetricUtil.Set(new PullingMessageCountMetric { MessageSize = messageSize, Consumer = request.Uri }, messageCount);
                }
#if DEBUG
                var messageCount1 = (chunk == null || chunk.Messages == null) ? 0 : chunk.Messages.Count;
                debugLog.Write(string.Format("End servicepulling->{0} messagecount->{1} time->{2}", index, isNotMessage ? "0" : messageCount1.ToString(), milliseconds), request.Uri);
#endif
                MetricUtil.Set(new PullingResponseCountMetric
                {
                    Consumer = request.Uri,
                    ServerHostName = server.ServerName,
                    Latency = milliseconds,
                    StatusCode = statuscode.ToString(),
                    HasMessages = isNotMessage?"0":"1",
                    Error = errortype
                });
                MetricUtil.Set(new PullingResponseLatencyMetric { ServerHostName = server.ServerName, Consumer = request.Uri }, milliseconds);
            }
            return chunk;
        }
 /// <summary>
 /// 往SERVER请求加载数据
 /// </summary>
 /// <param name="uri"></param>
 /// <param name="batchSize"></param>
 /// <param name="receiveTimeout"></param>
 /// <param name="server"> </param>
 /// <param name="sync"> </param>
 private SubChunk Pulling(string uri, int batchSize, int receiveTimeout, PhysicalServer server,bool sync= true)
 {
     SubChunk chunk = null;
     try
     {
         _serverPool.AcquireTimeout = receiveTimeout;
         var service = _serverPool.Acquire(); //SERVER线程数控制
         if (MomoryManager.IsOutOfMaxMemorySize)
         {
             Logg.Write("当前内存超过最大使用内存", LogLevel.Warn, "Consumer.InputBuffer.LoadData",
                          new[]
                              {
                                  new KeyValue {Key = "Consumer", Value = uri},
                                  new KeyValue
                                      {Key = "MaxMemorySize", Value = MomoryManager.MaxMemorySize.ToString()},
                                  new KeyValue
                                      {
                                          Key = "CurrentMemorySize",
                                          Value = MomoryManager.CurrentMemorySize.ToString()
                                      }
                              });
             _serverPool.Release();
             return null; //内存控制
         }
         if (_cts != null && _cts.IsCancellationRequested)
         {
             _serverPool.Release();
             return null;
         }
         try
         {
             chunk = service.Pulling(server,
                                     receiveTimeout,
                                     new PullingRequest
                                         {
                                             AckTimeout = AckTimeout,
                                             Timestamp = Time.ToTimestamp(),
                                             Uri = uri,
                                             BatchSize = batchSize,
                                             ClientIP = Local.IPV4,
                                             ClientVersion = Version.AssemblyMajorVersion,
                                             Platform = "NET"
                                         });
             if (chunk == null) throw new Exception("pulling return null");
         }
         catch
         {
             MetricUtil.Set(new ExceptionCountMetric { Consumer = uri, HappenedWhere = ExceptionType.OnPulling.ToString() });
             throw;
         }
         finally
         {
             _serverPool.Release();
             _serverUriManager.EndPulling(uri, server,(chunk != null && chunk.Messages != null && chunk.Messages.Count > 0));
         }
     }
     catch (Exception ex)
     {
         Logg.Write(ex, LogLevel.Error,
                      Consts.Consumer_Title_LoadDataError,
                      new[]
                          {
                              new KeyValue {Key = "PullingRequestUri", Value = uri},
                              new KeyValue {Key = "ServerUri", Value = server == null ? "" : server.ServerDomainName},
                          });
     }
     return chunk;
 }