Ejemplo n.º 1
0
        public static bool Dequeue <T>(string queueName, MemcachedQueueItemHandler <T> handler, uint maxRetryTimes = 2)
        {
            if (maxRetryTimes < 0)
            {
                maxRetryTimes = 0;
            }
            if (string.IsNullOrWhiteSpace(queueName))
            {
                throw new ArgumentNullException("queueName");
            }
            if (CheckSerializable(typeof(T)) == false)
            {
                throw new NotSupportedException("不支持handler消息处理返回结果的类型\"" + typeof(T).FullName + "\",因为此类型不支持二进制序列化,如果要使用请在此类型的定义处添加[Serializable]的Attribute");
            }

            queueName = queueName.Trim();
            string queueId = GetQueueId(queueName);

            if (string.IsNullOrWhiteSpace(queueId)) // 队列列表里未找到指定名称的队列
            {
                //handler(-1, null);
                return(false);
            }
            ulong en_index = GetIndexById(queueId, MemcachedQueueIndexType.Enqueue);

            if (en_index <= 0) // 还从未写入过,那么肯定读取不到数据了
            {
                //handler(-1, null);
                return(false);
            }
            var client = MemcachedCacheHelper.GetClientInstance();

            MemcachedClient.CasResult casRst;
            ulong              unique;
            string             k1 = BuildIndexKey(queueId, MemcachedQueueIndexType.Dequeue);
            ulong              de_index;
            MemcachedQueueItem item;

            do
            {
                string de_index_string = client.Gets(k1, out unique) as string; // 读取上次出队列的index
                if (string.IsNullOrWhiteSpace(de_index_string) ||
                    de_index_string == "0" ||
                    ulong.TryParse(de_index_string, out de_index) == false ||
                    de_index <= 0)
                {
                    // 说明之前还从未出过队列
                    de_index = 0;
                }
                de_index++;              // 计算出本次应该出队列的item的index
                if (de_index > en_index) // 如果已经超过队列最近入的index了,说明队列已经读空了(所有item都读取过了)
                {
                    //handler(-1, null);
                    return(false);
                }
                string itemKey = BuildItemIndexKey(queueId, de_index);
                item = client.Get(itemKey) as MemcachedQueueItem;
                // 这里可能遇到一种极端的读写并发情况,即在Enqueue方法的地方先将EnqueueIndex增长上去了,但此index处的元素数据还未来得及Set写
                // 然后本Dequeue方法就先拿到此index,然后Get数据,结果因为Set还未来得及写入所以获取数据未空
                // 所以需要等待一会儿再试(期望这段等待时间内写入线程能完成此index处的数据的写入)
                int reTryTimes = 0;
                while (item == null)
                {
                    Thread.Sleep(10); // 等待10ms再试着获取此index处的数据
                    item = client.Get(itemKey) as MemcachedQueueItem;
                    reTryTimes++;
                    if (reTryTimes >= maxRetryTimes) // 如果重试达到maxRetryTimes次(累计maxRetryTimes * 10ms了),那么基本上证明不是并发还未写入了,因为并发写入问题不可能等这么久还未写入进去,基本上确定是遇到网络问题或脏数据,或者memcached服务挂了
                    {
                        break;
                    }
                    // 基本上Dequeue操作都是单线程慢慢处理,所以对执行时间要求不是那么高,500ms的延迟等待可以接受(并且是要遇到item==null的很极端的读写并发,或者是遇到了脏数据,发生的几率都很小)
                }
                if (unique == 0) // 说明还不存在,需要新增
                {
                    casRst = client.Add(k1, de_index.ToString(CultureInfo.InvariantCulture)) ? MemcachedClient.CasResult.Stored : MemcachedClient.CasResult.NotStored;
                }
                else
                {
                    casRst = client.CheckAndSet(k1, de_index.ToString(CultureInfo.InvariantCulture), unique); // 乐观锁,满足条件(没有人优先改动过)则更新缓存,如果更新成功则锁定了de_index这个索引值,其它线程无法再获取到这个index的item了
                }
            } while
            (
                casRst != MemcachedClient.CasResult.Stored || // 乐观锁,这种写法防止并发冲突
                item == null  // 发生item == null这种情况可能是脏数据了,则继续找下一个索引处的元素
            );
            if (item == null) // 说明没有找到有效数据
            {
                //handler(-1, null);
                return(false);
            }

            string    itemProcessdResultKey = BuildItemIndexKeyForProcessResult(queueId, de_index);
            T         rst = default(T);
            Exception ex  = null;

            try
            {
                rst = handler((long)de_index, item);
                ex  = null;
            }
            catch (Exception ex1)
            {
                rst = default(T);
                ex  = ex1;
                throw;
            }
            finally
            {
                MemcachedQueueItemProcessResult pr = new MemcachedQueueItemProcessResult {
                    Exception = ex, Result = rst
                };
                client.Set(itemProcessdResultKey, pr, DateTime.Now.AddMinutes(120));
            }
            return(true);
        }
Ejemplo n.º 2
0
        public static MemcachedQueueItem GetItemWithProcessResult(string queueName, long index, out MemcachedQueueItemProcessResult processResult)
        {
            if (string.IsNullOrWhiteSpace(queueName))
            {
                throw new ArgumentNullException("queueName");
            }
            if (index <= 0)
            {
                processResult = null;
                return(null);
            }
            queueName = queueName.Trim();
            var queueId = GetQueueId(queueName);

            if (string.IsNullOrWhiteSpace(queueId))
            {
                processResult = null;
                return(null);
            }
            string             itemKey = BuildItemIndexKey(queueId, (ulong)index);
            string             itemProcessdResultKey = BuildItemIndexKeyForProcessResult(queueId, (ulong)index);
            var                client = MemcachedCacheHelper.GetClientInstance();
            var                objs   = client.Get(new string[] { itemKey, itemProcessdResultKey });
            MemcachedQueueItem item   = null;

            processResult = null;
            if (objs != null)
            {
                if (objs.Length > 0)
                {
                    item = objs[0] as MemcachedQueueItem;
                }
                if (objs.Length > 1)
                {
                    processResult = objs[1] as MemcachedQueueItemProcessResult;
                }
            }
            return(item);
        }