WCF通道和通道工厂缓存

本文关键字:通道 缓存 工厂 WCF | 更新日期: 2023-09-27 18:11:46

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

WCF通道和通道工厂缓存

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

您是否需要多渠道工厂(即…(是否有多种服务)?根据我的经验,这是您在性能方面看到最大好处的地方。创建一个频道是一个相当便宜的任务;一开始就把一切准备好需要时间。

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

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

当我有更多的时间时,我会发布一些示例代码。

更新:代码示例

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

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

字典如下(object是值,因为它将包含不同的通道工厂,每个服务一个)。我把"Cache"放在例子中作为占位符——用你正在使用的任何缓存机制替换语法。

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

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

你总是可以把你的ChannelFactory设置为每个WCF合约的静态…

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

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

创建通道会消耗很多性能。实际上,如果您在客户端中使用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 { }
}

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