21

所以我决定在我的 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();
        }
    }
}
4

3 回答 3

22

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

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

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

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

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

更新:代码示例

这是我如何为工作中的项目实施此示例的示例。我使用ChannelFactory<T>的是 ,因为我正在开发的应用程序是一个具有多个服务的 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;
    }
}

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

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% 的问题通常都与应用程序之外的事物有关(外部客户端或不受我们直接控制的数据源)。

让我知道是否有任何不清楚或您有其他问题。

于 2011-10-20T20:36:18.837 回答
5

你总是可以让你的 ChannelFactory 为每个 WCF 合同静态...

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

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

于 2011-10-21T06:18:49.550 回答
3

创建 Channel 会耗费如此多的性能。实际上,如果你在客户端使用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 方法的逻辑和参数。但是这个启动器类不仅限于一个端点。它对您应用程序中的所有端点都非常强大。希望。它对你很有效。顺便提一句。这个解决方案不是我的。我从一本书中得到这个。

于 2012-04-02T14:40:58.437 回答