在 WCF 中缓存?

Posted

技术标签:

【中文标题】在 WCF 中缓存?【英文标题】:Caching in WCF? 【发布时间】:2010-10-29 15:46:39 【问题描述】:

我正在构建 WCF 服务。我需要将参考数据存储在缓存中,每次我收到来自该方法的输入时都会查找这些数据……正确的方法是什么?我还想为缓存定义一个过期策略,在一定时间间隔后使其失效。

【问题讨论】:

social.msdn.microsoft.com/Forums/en-US/wcf/thread/… 【参考方案1】:

如果您使用的是 .NET 4,推荐的方法是使用 MemoryCache

【讨论】:

看看这篇博客文章有一个如何使用它的例子:pieterderycke.wordpress.com/2012/04/09/… 宏伟的解决方案,无需 xml,无需 Web 配置。 只要确保处理掉你的内存缓存,否则你将有一个很好的内存泄漏。我知道...这让我失去了几天的生命。 我只是想知道您是否使用 MemoryCache 为什么需要处理从缓存中处理对象? CachePolicy 不是在处理这个吗?【参考方案2】:

任何缓存解决方案都应该解决两个基本问题

1) 缓存项的存储和检索

2) 缓存失效

由于 Http 缓存是众所周知的,我不打算详细解释它。您可以将 asp 兼容性属性单独与一些 Web 配置一起使用,您将在其中通过魅力获得缓存。

[AspNetCacheProfile("MyProfile")]
        public Customer GetName(string id)
        
             // ...
        

网络配置就像

<system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    
</system.serviceModel>
<system.web>
   <caching>
      <outputCacheSettings>
         <outputCacheProfiles>
            <add name=" MyProfile" duration="600" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
         </outputCacheProfiles>
      </outputCacheSettings>
   </caching>
</system.web>

但这不适用于大多数场景,尤其是当您有大型复杂对象要缓存时。例如我有一种情况,我想缓存一个系统生成的图像(操作合约的输出是一个系统生成的图像,它依赖于输入)。 在这种情况下,您必须实现自己的缓存。我使用了 Microsoft 企业库缓存块来满足我所有的缓存存储需求。但是,您仍然需要进行管道以将 Microsoft 企业库缓存块与您的 WCF 服务集成。首先你必须拦截 WCF 通信通道来实现缓存。可以在http://msdn.microsoft.com/en-us/magazine/cc163302.aspx 找到有关如何拦截 WCF 通信通道的详细讨论。 这就是你为 WCF 缓存做管道的方式

第 0 步 假设您有一个如下操作合约,并且您想缓存该方法返回的项目。

[OperationContract]
MyCompositeClass Rotate(int angle)

第 1 步 首先,您必须在 WCF 管道中注册您的自定义缓存器。为此,我将使用一个属性,以便我可以根据面向方面的编程原则很好地装饰我的 WCF 调用。

using System;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Reflection;

    [AttributeUsage(AttributeTargets.Method)]
    public class MyCacheRegister : Attribute, IOperationBehavior
    
        ConstructorInfo _chacherImplementation;
        public ImageCache(Type provider)
        
            if (provider == null)
            
                throw new ArgumentNullException("Provider can't be null");
            
            else if (provider.IsAssignableFrom(typeof(IOperationInvoker)))
            
                throw new ArgumentException("The type " + provider.AssemblyQualifiedName + " does not implements the interface " + typeof(IOperationInvoker).AssemblyQualifiedName);
            
            else
            
                try
                
                    Type[] constructorSignatureTypes = new Type[1];
                    constructorSignatureTypes[0] = typeof(IOperationInvoker);
                    _chacherImplementation = provider.GetConstructor(constructorSignatureTypes);

                
                catch
                
                    throw new ArgumentException("There is no constructor in " + provider.AssemblyQualifiedName + " that accept " + typeof(IOperationInvoker).AssemblyQualifiedName + " as a parameter");
                

            


        

        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        
            return;
        

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        
            return;
        

        /// <summary>
        /// Decorate the method call with the cacher
        /// </summary>
        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        
            //decorator pattern, decorate with a  cacher
            object[] constructorParam = new object[1];
            constructorParam[0] = dispatchOperation.Invoker;
            dispatchOperation.Invoker = (IOperationInvoker)_chacherImplementation.Invoke(constructorParam);
        

        public void Validate(OperationDescription operationDescription)
        
            return;
        
    

第 2 步

然后你必须实现缓存对象将被检索的点。

using System;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Common;
using System.IO;

    class RotateCacher : IOperationInvoker
    

        private IOperationInvoker _innerOperationInvoker;
        public RotateImageCacher(IOperationInvoker innerInvoker)
        
            _innerOperationInvoker = innerInvoker;
        
        public object[] AllocateInputs()
        
            Object[] result = _innerOperationInvoker.AllocateInputs();
            return result;
        

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        
            object result=null;

///TODO: You will have more object in the input if you have more ///parameters in your method

            string angle = inputs[1].ToString();

            ///TODO: create a unique key from the inputs
            string key = angle;

            string provider = System.Configuration.ConfigurationManager.AppSettings["CacheProviderName"];
            ///Important Provider will be DiskCache or MemoryCache for the moment
provider =”DiskCache”;
///TODO: call enterprise library cache manager, You can have your own 
/// custom cache like Hashtable

    ICacheManager manager = CacheFactory.GetCacheManager(provider);

            if (manager.Contains(key))
            

                result =(MyCompositeClass) manager[key];

            
            else
            
                result =(MyCompositeClass) _innerOperationInvoker.Invoke(instance, inputs, out outputs);
                manager.Add(key, result);
            
            return result;
        

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        
            IAsyncResult result = _innerOperationInvoker.InvokeBegin(instance, inputs, callback, state);
            return result;
        

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult asyncResult)
        
            object result = _innerOperationInvoker.InvokeEnd(instance, out outputs, asyncResult);
            return result;
        

        public bool IsSynchronous
        
            get  return _innerOperationInvoker.IsSynchronous; 
        
    

第 3 步

最后在您的服务调用上方添加您的属性

[OperationContract]
[MyCacheRegister(typeof(RotateCacher)]
MyCompositeClass Rotate(int angle)

企业库缓存块的配置超出了这个答案的范围。您可以使用以下链接来学习它。企业库的好处是您可以使用现成的方法来扩展缓存策略。它内置了缓存到期和存储的方法。您还可以编写自己的缓存过期和存储策略。 http://www.codeproject.com/KB/web-cache/CachingApplicationBlock.aspx

最后一件事,为了让您的企业库缓存正常工作,您需要添加以下配置详细信息。您还需要将相关的 dll 添加到您的项目参考中。

<configSections>
    <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  </configSections>


  <cachingConfiguration defaultCacheManager="Cache Manager">
    <cacheManagers>
      <add name="MemoryCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000"
        numberToRemoveWhenScavenging="10" backingStoreName="NullBackingStore" />
      <add name="DiskCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000"
        numberToRemoveWhenScavenging="10" backingStoreName="IsolatedStorageCacheStore" />
    </cacheManagers>
    <backingStores>
      <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        name="NullBackingStore" />
      <add name="IsolatedStorageCacheStore" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.IsolatedStorageBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        encryptionProviderName="" partitionName="MyCachePartition" />
    </backingStores>
  </cachingConfiguration>

【讨论】:

我希望您不打算将此答案复制粘贴到您在网站上找到的每个旧缓存问题。考虑将一些问题标记为重复。 我不知道有这样的选择。该怎么做? 每个问题下方都有一个标记链接。 如何获取问题 ID,将其他人标记为重复我需要问题 ID(获取 UI 询问问题 ID) 每个帖子下方都有一个link 链接(它是一个永久链接)。您也可以在浏览器窗口中使用 URL。【参考方案3】:

您可以查看Velocity。这是微软的分布式内存caching framework。但这可能有点太beta了...

【讨论】:

【参考方案4】:

这是一篇好文章:http://cascadeofinsights.com/post/1410736927/introducing-attribute-based-caching

【讨论】:

【参考方案5】:

如果您要在负载平衡的无状态系统中横向扩展至多台服务器,您需要design for use of a distributed cache。这里要做的主要事情是:

    同时使用本地和分布式缓存。只放会话或短 生活在分布式缓存中的东西,其他东西在本地缓存。

    为项目设置适当的超时。这将取决于 信息的类型以及它需要离源多近。

    当你知道它会失禁时从缓存中删除它(比如 更新、删除等)。

    注意设计唯一的缓存键。建立一个模型 您计划缓存的信息类型并将其用作模板 构建密钥。

【讨论】:

【参考方案6】:

您可以使用 System.Web.Cache(即使您不在网络环境中),这就是我要做的。它基本上是一个很大的内存哈希表,带有一些用于过期内容的细节。

【讨论】:

要么接受,要么放弃......但这直接来自 msdn: Cache 类不适合在 ASP.NET 应用程序之外使用。它是为在 ASP.NET 中使用而设计和测试的,以便为 Web 应用程序提供缓存。在其他类型的应用程序中,例如控制台应用程序或 Windows 窗体应用程序,ASP.NET 缓存可能无法正常工作。【参考方案7】:

有很多方法可以做到这一点。一种相当简单的方法是自己托管 System.Web.Cache 对象并使用它来存储引用数据。这里有一个很好的例子:http://kjellsj.blogspot.com/2007/11/wcf-caching-claims-using.html

【讨论】:

正如 Simon 所指出的,MS 说“Cache 类不适合在 ASP.NET 应用程序之外使用。它是为在 ASP.NET 中使用而设计和测试的,以便为 Web 应用程序提供缓存。在其他方面应用程序类型,例如控制台应用程序或 Windows 窗体应用程序,ASP.NET 缓存可能无法正常工作。"【参考方案8】:

WCF REST Starter Kit 具有缓存功能,这里有一篇关于使用它的文章...带有示例代码。

http://weblogs.asp.net/gsusx/archive/2008/10/29/adding-caching-to-wcf-restful-services-using-the-rest-starter-kit.aspx

【讨论】:

【参考方案9】:

您实际上可以确保在缓存的基础数据发生更改时使缓存失效,而不是每隔一段时间就使缓存数据过期。

请参阅 info Q 中的此示例 http://www.infoq.com/news/2011/04/Attribute-Caching

[Cache.Cacheable("UserTransactionCache")]
public DataTable GetAllTransactionsForUser(int userId)

    return new DataProvider().GetAllTransactionsForUser(userId);


[Cache.TriggerInvalidation("UserTransactionCache")]
public void DeleteAllTransactionsForUser(int userId)

 ...

【讨论】:

以上是关于在 WCF 中缓存?的主要内容,如果未能解决你的问题,请参考以下文章

在 Web 场环境中使用共享缓存来检测 WCF 中的重放攻击

为啥缓存 WCF 通道是一件坏事?

在WCF数据访问中使用缓存提高Winform字段中文显示速度的方法

在WCF数据访问中使用缓存提高Winform字段中文显示速度的方法

WCF REST (WebHttpBinding) 可以遵守 PROGRAMMATIC 输出缓存策略吗?

WCF 缓存列表错误