【问题标题】:WCF Channel and ChannelFactory CachingWCF 通道和 ChannelFactory 缓存
【发布时间】:2011-12-12 02:35:23
【问题描述】:

所以我决定在我的 WCF 应用程序中提高一点性能,并尝试缓存 Channels 和 ChannelFactory。在开始之前,我有两个关于这一切的问题需要弄清楚。

1) ChannelFactory 是否应该实现为单例?

2) 我有点不确定如何缓存/重用各个频道。你有什么例子可以分享吗?

请务必注意,我的 WCF 服务正在部署为一个独立的应用程序,只有一个端点。

编辑:

感谢您的回复。不过我还有几个问题...

1) 我想我对应该在哪里进行缓存感到困惑。我正在向我们公司的另一个部门提供使用此代码的客户端 API。这种缓存是否发生在客户端上?

2) 客户端 API 将用作 Silverlight 应用程序的一部分,这会改变什么吗?具体来说,在这种情况下有哪些缓存机制可用?

3)我还不清楚GetChannelFactory方法的设计。如果我只有一项服务,是否应该只创建和缓存一个 ChannelFactory?

我还没有实现任何缓存功能(因为我完全不知道应该如何完成它!),但到目前为止,我为客户端代理提供了以下功能:

namespace MyCompany.MyProject.Proxies
{
    static readonly ChannelFactory<IMyService> channelFactory =
        new ChannelFactory<IMyService>("IMyService");

    public Response DoSomething(Request request)
    {
        var channel = channelFactory.CreateChannel();

        try
        {
            Response response = channel.DoSomethingWithService(request);
            ((ICommunicationObject)channel).Close();
            return response;
        }
        catch(Exception exception)
        {
            ((ICommenicationObject)channel).Abort();
        }
    }
}

【问题讨论】:

  • 对于#3,是的,应该只创建一个通道工厂。基本上,您拥有的每个服务终端都将拥有一个通道工厂。就我而言,到目前为止,我们大约有 6 个,主要分布在 2 个层级。在您的情况下,如果您只拥有一项服务,由一个应用程序使用,您可以简单地执行您在上面所做的事情。你上面的代码是在正确的轨道上。缓存将基于应用的需求。
  • @Tim - 感谢您的所有帮助。我真的很感激!我认为在我的情况下,我的服务如此“简单”的事实让我在查看这些其他有多个端点的示例时感到困惑。我=现在不那么困惑了=蒂姆解释得很好!谢谢老兄!
  • 不客气。很高兴能帮上忙 - 编码愉快!

标签: c# .net wcf channel channelfactory


【解决方案1】:

使用 ChannelFactory 创建工厂的实例,然后缓存该实例。然后,您可以根据需要/希望从缓存的 istance 创建通信通道。

您是否需要多个渠道工厂(即是否有多种服务)?以我的经验,这就是你会看到性能的最大好处的地方。创建频道是一项相当便宜的任务。一开始就设置好一切都需要时间。

我不会缓存单个频道 - 我会创建它们,将它们用于操作,然后关闭它们。如果您缓存它们,它们可能会超时并且通道会出现故障,那么您将不得不中止它并创建一个新的。

不知道为什么要使用单例来实现 ChannelFactory,特别是如果您要创建它并缓存它,并且只有一个端点。

稍后我会在有更多时间时发布一些示例代码。

更新:代码示例

以下是我如何在工作中的项目中实现此功能的示例。我使用了ChannelFactory&lt;T&gt;,因为我正在开发的应用程序是一个具有多个服务的 n 层应用程序,并且会添加更多服务。目标是有一种简单的方法在应用程序的每个生命周期中创建一次客户端,然后根据需要创建通信渠道。这个想法的基础不是我的(我从网上的一篇文章中得到),但我根据自己的需要修改了实现。

我的应用程序中有一个静态帮助程序类,在该类中我有一个字典和一个从 channelf 工厂创建通信通道的方法。

字典如下(对象是值,因为它将包含不同的通道工厂,每个服务一个)。我在示例中将“缓存”作为一种占位符 - 用您正在使用的任何缓存机制替换语法。

public static Dictionary<string, object> OpenChannels
{
    get
    {
        if (Cache["OpenChannels"] == null)
        {
            Cache["OpenChannels"] = new Dictionary<string, object>();
        }

        return (Dictionary<string, object>)Cache["OpenChannels"];
    }
    set
    {
        Cache["OpenChannels"] = value;
    }
}

Next 是一种从工厂实例创建通信通道的方法。该方法首先检查工厂是否存在 - 如果不存在,则创建它,将其放入字典中,然后生成通道。否则它只是从工厂的缓存实例中生成一个通道。

public static T GetFactoryChannel<T>(string address)
{

    string key = typeof(T.Name);

    if (!OpenChannels.ContainsKey(key))
    {
        ChannelFactory<T> factory = new ChannelFactory<T>();
        factory.Endpoint.Address = new EndpointAddress(new System.Uri(address));
        factory.Endpoint.Binding = new BasicHttpBinding();
        OpenChannels.Add(key, factory);
    }

    T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel();

    ((IClientChannel)channel).Open();

    return channel;
}

我已经从我在工作中使用的部分中删除了这个示例。您可以在此方法中做很多事情 - 您可以处理多个绑定、分配用于身份验证的凭据等。它几乎是您生成客户端的一站式购物中心。

最后,当我在应用程序中使用它时,我通常会创建一个通道,处理我的业务,​​然后关闭它(或在需要时中止它)。例如:

IMyServiceContract client;

try
{
    client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress");

    client.DoSomething();

    // This is another helper method that will safely close the channel, 
    // handling any exceptions that may occurr trying to close.
    // Shouldn't be any, but it doesn't hurt.
    Helper.CloseChannel(client);
}
catch (Exception ex)
{
    // Something went wrong; need to abort the channel
    // I also do logging of some sort here
    Helper.AbortChannel(client);
}

希望以上示例能给您一些帮助。我已经在生产环境中使用类似的东西大约一年了,而且效果很好。我们遇到的 99% 的问题通常都与应用程序之外的事物有关(外部客户端或不受我们直接控制的数据源)。

如果有任何不清楚的地方或您还有其他问题,请告诉我。

【讨论】:

  • 谢谢蒂姆。你确实提供了一些有价值的信息。我一定会留意你的榜样!
  • @user384080 - 代码在我的回答中。如果不清楚,请告诉我。谢谢。
  • @Tim 您的实现中有一个错误。无论地址是什么,您都可以按合约类型缓存工厂。您应该有一个包含合同类型和地址的密钥。
  • 似乎缺少异常处理。你不知道是谁抛出了异常(可能是 DoSomething),那么为什么你决定需要中止通道而不是仅仅关闭它?
  • @Cesar - 这是我在工作中使用的代码。在工作中,还有更多工作要做,我在示例代码// I also do logging of some sort here 中添加了注释。
【解决方案2】:

对于每个 WCF 合同,您总是可以将 ChannelFactory 设为静态...

您应该知道,从 .Net 3.5 开始,代理对象是出于性能原因由通道工厂池化的。调用ICommunicationObject.Close() 方法实际上将对象返回到池中,希望它可以被重用。

如果您想进行一些优化,我会查看分析器,如果您可以防止在代码中只进行一次 IO 调用,那么它可能远远超过您使用通道工厂进行的任何优化。不要选择要优化的区域,使用分析器找到可以优化的目标。例如,如果您有一个 SQL 数据库,您可能会在查询中发现一些唾手可得的成果,如果这些尚未优化的话,这些成果将使您获得数量级的性能提升。

【讨论】:

    【解决方案3】:

    创建频道会耗费大量性能。实际上,如果你在客户端使用ClientBase而不是纯ChannelFactory,WCF已经有了ChannelFactory的缓存机制。但是如果您进行一些其他操作,缓存将过期(如果需要,请搜索详细信息)。 对于 ErOx 的问题,我有另一个解决方案,我认为它更好。见下文:

    
    namespace ChannelFactoryCacheDemo
    {
        public static class ChannelFactoryInitiator
        {
            private static Hashtable channelFactories = new Hashtable();
    
            public static ChannelFactory Initiate(string endpointName)
            {
                ChannelFactory channelFactory = null;
    
                if (channelFactories.ContainsKey(endpointName))//already cached, get from the table
                {
                    channelFactory = channelFactories[endpointName] as ChannelFactory;
                }
                else // not cached, create and cache then
                {
                    channelFactory = new ChannelFactory(endpointName);
                    lock (channelFactories.SyncRoot)
                    {
                        channelFactories[endpointName] = channelFactory;
                    }
                }
                return channelFactory;
            }
        }
        class AppWhereUseTheChannel
        {
            static void Main(string[] args)
            {
                ChannelFactory channelFactory = ChannelFactoryInitiator.Initiate("MyEndpoint");
            }
        }
    
        interface IMyContract { }
    }
    
    

    如果您有其他需求,您可以自己自定义 Initiate 方法的逻辑和参数。但是这个启动器类不仅限于一个端点。它对您应用程序中的所有端点都非常强大。希望。它对你很有效。顺便提一句。这个解决方案不是我的。我是从一本书中得到的。

    【讨论】:

    • 请注意lock 使用不正确。对ContainsKey 的调用也应该锁定。
    猜你喜欢
    • 2013-01-10
    • 2011-01-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 2010-12-22
    • 2012-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多