WCF通道工厂缓存

时间:2015-06-08 20:50:06

标签: c# wcf

WCF服务将使用另一个Wcf服务。现在,我想创建通道工厂对象并手动缓存它。我知道表现会很好,但会引起任何其他问题的关注。

我发现信息如下:

“使用ChannelFactory,您仍然可以使用自己的自定义MRU缓存实现通道工厂缓存。这仍然意味着一个重要的限制:对共享通道工厂的同一服务端点的调用也必须共享相同的凭据。这意味着您不能为从Web服务器层调用应用程序服务的每个线程传递不同的凭据。一个不存在问题的情况是,如果您使用相同的证书或Windows凭据对下游服务进行身份验证。在这种情况下,如果您需要传递有关经过身份验证的用户,您可以使用自定义标头而不是安全令牌。“

链接:http://devproconnections.com/net-framework/wcf-proxies-cache-or-not-cache

我在Google中找到了一个示例代码,如下所示。

internal delegate void UseServiceDelegate<in T>(T proxy);



    internal static class Service<T>
   {

      private static readonly IDictionary<Type, string>  
                 cachedEndpointNames   =    new  Dictionary<Type, string>();


private static readonly IDictionary<string, ChannelFactory<T>> 
    cachedFactories =
    new Dictionary<string, ChannelFactory<T>>();


     internal static void Use(UseServiceDelegate<T> codeBlock)
     {
        var factory = GetChannelFactory();
        var proxy = (IClientChannel)factory.CreateChannel();
        var success = false;

      try
      {
        using (proxy)
        {
            codeBlock((T)proxy);
        }

        success = true;
    }
    finally
    {
        if (!success)
        {
            proxy.Abort();
        }
    }
}


     private static ChannelFactory<T> GetChannelFactory()
     {
      lock (cachedFactories)
      {
        var endpointName = GetEndpointName();

        if (cachedFactories.ContainsKey(endpointName))
        {
            return cachedFactories[endpointName];
        }

        var factory = new ChannelFactory<T>(endpointName);

        cachedFactories.Add(endpointName, factory);
        return factory;
        }
    }


     private static string GetEndpointName()
     {
        var type = typeof(T);
        var fullName = type.FullName;

    lock (cachedFactories)
    {
        if (cachedEndpointNames.ContainsKey(type))
        {
            return cachedEndpointNames[type];
        }

        var serviceModel =  

     ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
     .SectionGroups["system.serviceModel"] as ServiceModelSectionGroup;

        if ((serviceModel != null) && !string.IsNullOrEmpty(fullName))
        {
            foreach (var endpointName in  
    serviceModel.Client.Endpoints.Cast<ChannelEndpointElement>()
   .Where(endpoint => fullName.EndsWith(endpoint.Contract)).Select(endpoint 
    => endpoint.Name))
            {
                cachedEndpointNames.Add(type, endpointName);
                return endpointName;
            }
        }
    }

    throw new InvalidOperationException("Could not find endpoint element   
       for type '" + fullName + "' in the ServiceModel client 
      configuration section. This might be because no configuration file 
       was found for your application, or because no endpoint element 
       matching this name could be found in the client element.");
    }
 }

我完全糊涂了该怎么办。谁能给我一个最佳实践指南?

1 个答案:

答案 0 :(得分:5)

这是一个复杂的主题,需要详细介绍,但现在就可以了。

首先,作为一般规则,您应该缓存ChannelFactory而不是个人ChannelChannelFactory构造和线程安全都很昂贵,因此它是缓存的理想选择。 Channel构建起来很便宜,通常建议只根据需要创建频道并尽早关闭频道。此外,当您缓存Channel时,您必须担心它超时会导致它出错,这会使首先缓存它的全部好处失效。

Michele Leroux Bustamante 链接的article是其中最好的资源之一。正如她所说,Windows客户端和服务器端客户端之间存在差异。大多数情况下,只有Windows客户端受益于缓存,因为服务器端客户端上的凭据通常因线程而异。对于典型的Windows客户端,有两个主要选项:自己缓存引用或利用MRU缓存。

利用MRU缓存:从本质上讲,这意味着您要让微软采取行动。 ClientBase类将为内部ChannelFactory实例使用MRU缓存。缓存行为通过CacheSetting属性进行控制,默认情况下,如果任何&#34;安全敏感的&#34;属性被访问。 ClientBase属性会在访问时使MRU缓存中的ChannelFactory无效并从中删除Endpoint包括ClientCredentialsChannelFactoryCacheSettings本身。有一种方法可以通过将CacheSettings.AlwaysOn属性设置为Binding来覆盖此行为。此外,如果ChannelFactory是运行时定义的,则ChannelFactory不再是MRU缓存的候选者。请参阅more details here

自己缓存引用:这意味着您将自己保留一组ChannelFactory引用。您在问题中提供的代码段使用此方法。我见过并且无可否认地使用修改版本的最佳方法是 Darin Dimitrov 通过this related SO question。对于我们这些喜欢对缓存机制进行更细粒度控制的人来说,这就是使用方法。这通常在必须在运行时设置凭据时使用,就像互联网服务通常需要的那样。

同样,可以缓存客户端代理以提高性能 - Wenlong Dong 对此主题有article

(更新)如前所述,服务器端客户端在ChannelFactory缓存方面的选项非常有限。对于这个简短的讨论,我们假设我们的部署方案如下所示:

  

客户 - &gt;服务A - &gt;服务B

在此方案中,为了利用ChannelFactory缓存而使用的最有可能的方法是自己为客户端服务A 。这样, Service A 每次 Service A 需要调用 Service B 时,不必构建不同的ChannelFactory实例。但是,如果var currentState = true; $.getState = function(){ return currentState; } 的属性需要针对每个调用进行更改,那么这将不再合适。

当然,如果 Service A 是Singleton并且每次调用下游服务( Service B )都不需要新的凭据,那么这也是成立的,但Singleton服务有它们的自己的一系列性能问题。