Exemple #1
0
        public void AddInvokeInfo(InvokeStatistics objInvoke)
        {
            if (objInvoke == null || String.IsNullOrWhiteSpace(objInvoke.Key))
                return;
            if (!IsWriteStatistic)
                return;
           
            lock (objLock)
            {
                if (!_dic.ContainsKey(objInvoke.Key))
                {
                    _dic[objInvoke.Key] = new List<InvokeStatistics>(StatisticIntervalCount);
                }
                _dic[objInvoke.Key].Add(objInvoke);
                if (_dic[objInvoke.Key].Count == StatisticIntervalCount)
                {
                    Task.Factory.StartNew(this.CalculateStatisticAndWriteLog, _dic[objInvoke.Key]);
                    _dic[objInvoke.Key] = new List<InvokeStatistics>(StatisticIntervalCount);
                }
            }

        }
		// todo: 支持平滑过期(需要 AppFabric Cache 支持)

		/// <summary>
		/// 将指定项添加到 Cache 对象,该项具有绝对到期策略,将在 timeToLive 参数限定的时间间隔(从添加时间算起)后过期。 
		/// </summary>
		/// <param name="key">用于引用该项的缓存键。</param>
		/// <param name="value">要添加到缓存的项。</param>
		/// <param name="timeToLive">添加对象时与该对象到期时之间的时间间隔。</param>
		public bool SetItem(string key, object value, TimeSpan timeToLive)
		{
			if (String.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentNullOrWhiteSpaceException("key");
			}
            using (InvokeStatistics objInvoke = new InvokeStatistics("Set", key, 0))
            {

                return this.SetItemInternal(key, value, timeToLive) != null;
            }
			
		}
		public object GetAndSetItem(string key, Func<object, object> callback, object callBackState)
		{
			if (String.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentNullOrWhiteSpaceException("key");
			}

			using (InvokeStatistics objInvoke = new InvokeStatistics("GetAndSetItem", key, 0))
            {
				return this.GetAndSetItemInternal(key, callback, callBackState);
			}
			
		}
		/* 
		private class LocalCacheItem
		{
			public bool IsUpdating = false;

			public DataCacheItemVersion Version = null;

			public DateTime NextUpdateTime;

			public LocalCacheItem()
			{
			}
		}

		private class LocalCacheItemCallbackWrapper
		{
			public LocalCacheItem Item;

			public string Key;

			public DataCacheItem CachedItem;

			public Func<object, object> Callback;
			public object CallBackState;

			public TimeSpan TTL;

			public LocalCacheItemCallbackWrapper(LocalCacheItem item, string key, DataCacheItem cachedItem, Func<object, object> callback, object callBackState, TimeSpan timeToLive)
			{
				this.Item = item;
				this.Key = key;
				this.CachedItem = cachedItem;

				this.Callback = callback;
				this.CallBackState = callBackState;

				this.TTL = timeToLive;
			}
		}

		private TimeSpan asyncUpdateRetryInterval = TimeSpan.FromSeconds(60);		// 异步更新过程中发生错误或者回调函数返回值为 null 时再次尝试更新缓存项的时间间隔
		private TimeSpan asyncUpdateAdditionalLiveTime = TimeSpan.FromSeconds(60);  // 异步更新缓存项除更新间隔外额外附加的生存时间

		private void UpdateCacheItem(object state)
		{
			LocalCacheItemCallbackWrapper wrapper = (LocalCacheItemCallbackWrapper)state;
			try
			{
				object value = wrapper.Callback(wrapper.CallBackState);
				if (value != null)
				{
					// 注意: 这里在调用 SetItemInternal 更新缓存版本之前,必须先更新缓存项的下次更新时间,参考 GetAndSetItem 中对 localCacheItem.Version 和 cachedItem.Version 比较的相关说明
					wrapper.Item.NextUpdateTime = DateTime.Now + this.asyncUpdateInterval;

					wrapper.Item.Version = this.SetItemInternal(wrapper.Key, value, wrapper.TTL);
				}
				else
				{
					// 回调函数返回 null 时下次更新时间设为 asyncUpdateRetryInterval 和 asyncUpdateInterval 之间的较小者之后,以便在该时间间隔内命中该缓存项的请求不重复执行回调函数
					wrapper.Item.NextUpdateTime = DateTime.Now + (this.asyncUpdateRetryInterval < this.asyncUpdateInterval ? this.asyncUpdateRetryInterval : this.asyncUpdateInterval);

					IDebugableCachedItem valueAsDebugableItem = wrapper.CachedItem.Value as IDebugableCachedItem;
					if (valueAsDebugableItem != null)
					{
						valueAsDebugableItem.LastUpdateTime = DateTime.Now;
						valueAsDebugableItem.TimeToLive = (this.asyncUpdateRetryInterval < this.asyncUpdateInterval ? this.asyncUpdateRetryInterval : this.asyncUpdateInterval) + this.asyncUpdateAdditionalLiveTime;
					}

					// 重设当前缓存项的过期时间
					this.SetItemInternal(wrapper.Key, wrapper.CachedItem, (this.asyncUpdateRetryInterval < this.asyncUpdateInterval ? this.asyncUpdateRetryInterval : this.asyncUpdateInterval) + this.asyncUpdateAdditionalLiveTime);

					//this.dataCache.ResetObjectTimeout(wrapper.Key, TimeSpan.FromSeconds(Math.Min(this.asyncUpdateRetryInterval, this.asyncUpdateInterval) + this.asyncUpdateAdditionalLiveTime), this.regionName);
				}
			}
			catch (Exception err)
			{
				// 回调函数返回 null 时下次更新时间设为 asyncUpdateRetryInterval 和 asyncUpdateInterval 之间的较小者之后,以便在该时间间隔内命中该缓存项的请求不重复执行回调函数
				wrapper.Item.NextUpdateTime = DateTime.Now + (this.asyncUpdateRetryInterval < this.asyncUpdateInterval ? this.asyncUpdateRetryInterval : this.asyncUpdateInterval);

				try
				{
					IDebugableCachedItem valueAsDebugableItem = wrapper.CachedItem.Value as IDebugableCachedItem;
					if (valueAsDebugableItem != null)
					{
						valueAsDebugableItem.LastUpdateTime = DateTime.Now;
						valueAsDebugableItem.TimeToLive = (this.asyncUpdateRetryInterval < this.asyncUpdateInterval ? this.asyncUpdateRetryInterval : this.asyncUpdateInterval) + this.asyncUpdateAdditionalLiveTime;
					}

					// 重设当前缓存项的过期时间
					this.SetItemInternal(wrapper.Key, wrapper.CachedItem, (this.asyncUpdateRetryInterval < this.asyncUpdateInterval ? this.asyncUpdateRetryInterval : this.asyncUpdateInterval) + this.asyncUpdateAdditionalLiveTime);

					//this.dataCache.ResetObjectTimeout(wrapper.Key, TimeSpan.FromSeconds(Math.Min(this.asyncUpdateRetryInterval, this.asyncUpdateInterval) + this.asyncUpdateAdditionalLiveTime), this.regionName);
				}
				catch { }

				Container.LogService.Warn(
					String.Format("在更新缓存项{{CacheName={0},RegionName={1},Key={2}}}的过程中发生错误。", this.cacheName, this.regionName, wrapper.Key),
					XMS.Core.Logging.LogCategory.Cache, err
					);
			}
			finally
			{
				wrapper.Item.IsUpdating = false;
			}
		}

		private Dictionary<string, LocalCacheItem> cacheItemVersions = new Dictionary<string, LocalCacheItem>(StringComparer.InvariantCultureIgnoreCase);
		private System.Threading.ReaderWriterLockSlim lock4cacheItemVersions = new System.Threading.ReaderWriterLockSlim();

		private Dictionary<string, object> cacheItemLocks = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);

		public object GetAndSetItem(string key, Func<object, object> callback, object callBackState)
		{
			if (String.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentNullOrWhiteSpaceException("key");
			}

			if (!CacheSettings.Instance.distributeCacheSetting.PerformanceMonitor.Enabled)
			{
				return this.GetAndSetItemInternal(key, callback, callBackState);
			}
			else
			{
				InvokeInfo ii = new InvokeInfo(key, sw.Elapsed);

				object retValue = this.GetAndSetItemInternal(key, callback, callBackState);

				ii.EndTS = sw.Elapsed;

				List<InvokeInfo> invokes = null;

				lock (getAndSetItemInvokes)
				{
					getAndSetItemInvokes.Add(ii);

					if (getAndSetItemInvokes.Count >= CacheSettings.Instance.distributeCacheSetting.PerformanceMonitor.BatchCount)
					{
						invokes = getAndSetItemInvokes;

						getAndSetItemInvokes = new List<InvokeInfo>(CacheSettings.Instance.distributeCacheSetting.PerformanceMonitor.BatchCount);
					}
				}

				if (invokes != null)
				{
					WriteLog("GetAndSetItem", invokes);
				}

				return retValue;
			}
		}

		private object GetAndSetItemInternal(string key, Func<object, object> callback, object callBackState)
		{
			DataCacheItem cachedItem = this.GetItemInternal(key);

			LocalCacheItem localCacheItem;

			// 缓存项不存在时立即添加新的缓存项并返回
			if (cachedItem == null)
			{
				// 设采用开放式策略,防止阻塞,但可能多次设置
				object value = callback(callBackState);

				// 
				if (value is ReturnValue)
				{
					if (((ReturnValue)value).Code != ReturnValue.Code200)
					{
						return value;
					}
				}

				// 保证同时只有 1 个请求执行设置操作
				object cacheItemLock = null;

				lock (cacheItemLocks)
				{
					// 有正在设置的,那么立即返回
					if (cacheItemLocks.ContainsKey(key))
					{
						cacheItemLock = cacheItemLocks[key];

						return value;
					}
					else
					{
						cacheItemLock = new object();

						cacheItemLocks[key] = cacheItemLock;
					}
				}

				// 设置,这里有可能被设置多次,这是由于我们采取开放式策略决定的
				localCacheItem = new LocalCacheItem();
				localCacheItem.NextUpdateTime = DateTime.Now + this.asyncUpdateInterval; // 下次更新时间
				localCacheItem.IsUpdating = false;
				// 缓存项在缓存服务器中的生存时间为:异步更新间隔+5分钟,这足够保证缓存项能够及时成功更新
				// 同时,可以避免添加当前缓存项的客户端应用终止时(客户端应用中保存的 cacheItemVersions 失效,其它客户端因为仅读取该缓存项而没有存储其版本,永远不会更新该缓存项)
				// 缓存项将可能在很长时间(由传入的 timeToLive 值决定)内不再更新的问题
				localCacheItem.Version = this.SetItemInternal(key, value, this.asyncUpdateInterval + this.asyncUpdateAdditionalLiveTime);

				this.lock4cacheItemVersions.EnterWriteLock();
				try
				{
					this.cacheItemVersions[key] = localCacheItem;
				}
				finally
				{
					this.lock4cacheItemVersions.ExitWriteLock();
				}

				lock (cacheItemLocks)
				{
					cacheItemLocks.Remove(key);
				}

				return value;
			}

			this.lock4cacheItemVersions.EnterReadLock();
			try
			{
				localCacheItem = this.cacheItemVersions.ContainsKey(key) ? this.cacheItemVersions[key] : null;
			}
			finally
			{
				this.lock4cacheItemVersions.ExitReadLock();
			}

			// 首先不是处于更新过程中且时间上判断应更新缓存项,使用双重检查锁定机制并通过任务并行库异步更新缓存项
			if (localCacheItem != null)
			{
				//如果缓存服务器返回的缓存项的版本与本地存储的版本相同的话,说明服务器的缓存项是在本地设置的
				if (localCacheItem.Version == cachedItem.Version)
				{
					if (!localCacheItem.IsUpdating && localCacheItem.NextUpdateTime < DateTime.Now)
					{
						lock (localCacheItem) // 仅锁定当前缓存项,尽可能的避免阻塞对其它缓存项的访问
						{
							if (!localCacheItem.IsUpdating && localCacheItem.NextUpdateTime < DateTime.Now)
							{
								localCacheItem.IsUpdating = true;

								System.Threading.Tasks.Task.Factory.StartNew(this.UpdateCacheItem, new LocalCacheItemCallbackWrapper(localCacheItem, key, cachedItem, callback, callBackState, this.asyncUpdateInterval + this.asyncUpdateAdditionalLiveTime));
							}
						}
					}
				}
				else//缓存项已经被其它程序更新,移除 localCacheItem,以便仅在其它程序中维护它
				{
					// 以下两种情况会造成 localCacheItem 的版本与 cachedItem 的版本不相等
					//	1. 当前应用的其它运行实例或其它应用主动修改了同一键值的缓存
					//		这种情况下,localCacheItem.NextUpdateTime 和 localCacheItem.Version 之后将永远不会再被修改,因此,只要在超过 localCacheItem.NextUpdateTime 时从 cacheItemVersions 中移除当前键值对应的 localCacheItem 即可。
					//	2. 在从 cacheItemVersions 中获取到 localCacheItem 后并且执行到 localCacheItem.Version == cachedItem.Version 进行比较的过程中,恰好有另外一个线程命中
					//		这种情况下,localCacheItem.NextUpdateTime 通常大于 当前时间(因为 UpdateCacheItem 中更新 localCacheItem.Version 时会先更新其 NextUpdateTime 属性),但可能存在以下例外:
					//			对于更新间隔时间很小的情况(比如 0.1 秒),有可能因为在该间隔内从取到 localCacheItem 开始未执行到这里而造成 localCacheItem.NextUpdateTime 小于 当前时间。
					// 综上,假设程序在 1 分钟之内从取到 localCacheItem 开始一定能执行到这里(通常一定能满足),则只需要判断 localCacheItem.NextUpdateTime + 1 分钟 小于 当前时间 时移除 localCacheItem 即可。
					if (localCacheItem.NextUpdateTime.AddMinutes(1) < DateTime.Now)
					{
						this.lock4cacheItemVersions.EnterWriteLock();
						try
						{
							if (this.cacheItemVersions[key] == localCacheItem)
							{
								this.cacheItemVersions.Remove(key);
							}
						}
						finally
						{
							this.lock4cacheItemVersions.ExitWriteLock();
						}
					}
				}
			}

			// 在异步更新缓存项之前立即返回当前缓存项的值。
			return cachedItem.Value;
		}
		*/
		#endregion

		/// <summary>
		/// 从 Cache 对象中移除指定的缓存项。
		/// </summary>
		/// <param name="key">要移除的缓存项的键。</param>
		/// <returns>
		/// 移除成功,返回 <c>true</c>;移除失败,返回 <c>false</c>。
		/// </returns>
		public bool RemoveItem(string key)
		{
			if (String.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentNullOrWhiteSpaceException(key);
			}
            using (InvokeStatistics objInvoke = new InvokeStatistics("Remove", key, 0))
            {
                return this.client.Remove(this.BuildKey(key));
            }

		}
		/// <summary>
		/// 从 Cache 对象中获取指定的缓存项。
		/// </summary>
		/// <param name="key">要获取的缓存项的键。</param>
		/// <returns>要获取的缓存项对象。</returns>
		public object GetItem(string key)
		{
			if (String.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentNullOrWhiteSpaceException("key");
			}

			using (InvokeStatistics objInvoke = new InvokeStatistics("Get", key, 0))
            {
				DataCacheItem item = this.GetItemInternal(key);
				if (item != null)
				{
					return item.Value;
				}
				return null;
			}
		
		}