WCF 通道和 ChannelFactory 缓存

发布于 2024-12-10 19:22:33 字数 1176 浏览 0 评论 0原文

因此,我决定稍微提高 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();
        }
    }
}

So I've decided to up the performance a bit in my WCF application, and attempt to cache Channels and the ChannelFactory. There's two questions I have about all of this that I need to clear up before I get started.

1) Should the ChannelFactory be implemented as a singleton?

2) I'm kind of unsure about how to cache/reuse individual channels. Do you have any examples of how to do this you can share?

It's probably important to note that my WCF service is being deployed as a stand alone application, with only one endpoint.

EDIT:

Thank you for the responses. I still have a few questions though...

1)I guess I'm confused as to where the caching should occur. I'm delivering a client API that uses this code to another department in our company. Does this caching occur on the client?

2)The client API will be used as part of a Silverlight application, does this change anything? In particular, what caching mechanisms are available in such a scenario?

3)I'm still not clear about the design of the GetChannelFactory method. If I have only one service, should only one ChannelFactory ever be created and cached?

I still haven't implemented any caching feature (because I'm utterly confused about how it should be done!), but here's what I have for the client proxy so far:

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();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

伪装你 2024-12-17 19:22:33

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

您是否需要多个通道工厂(即是否有多个服务)?根据我的经验,这就是您将看到性能最大优势的地方。创建通道是一项相当便宜的任务;从一开始就将一切都设置好需要时间。

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

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

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

更新:代码示例

以下是我如何在工作项目中实现此功能的示例。我使用了 ChannelFactory,因为我正在开发的应用程序是一个包含多个服务的 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% 的问题通常都与应用程序外部的问题有关(外部客户端或不受我们直接控制的数据源)。

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

Use the ChannelFactory to create an instance of the factory, then cache that instance. You can then create communicatino channels as needed/desired from the cached istance.

Do you have a need for multiple channel factories (i.e.., are there multiple services)? In my experience, that's where you'll see the biggest benefit in performance. Creating a channel is a fairly inexpensive task; it's setting everything up at the start that takes time.

I would not cache individual channels - I'd create them, use them for an operation, and then close them. If you cache them, they may time out and the channel will fault, then you'll have to abort it and create a new one anyway.

Not sure why you'd want to usea singleton to implement ChannelFactory, especially if you're going to create it and cache it, and there's only one endpoint.

I'll post some example code later when I have a bit more time.

UPDATE: Code Examples

Here is an example of how I implemented this for a project at work. I used ChannelFactory<T>, as the application I was developing is an n-tier app with several services, and more will be added. The goal was to have a simple way to create a client once per life of the application, and then create communication channels as needed. The basics of the idea are not mine (I got it from an article on the web), though I modified the implementation for my needs.

I have a static helper class in my application, and within that class I have a dictionary and a method to create communication channels from the channelf factory.

The dictionary is as follows (object is the value as it will contain different channel factories, one for each service). I put "Cache" in the example as sort of a placeholder - replace the syntax with whatever caching mechanism you're using.

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 is a method to create a communication channel from the factory instance. The method checks to see if the factory exists first - if it does not, it creates it, puts it in the dictionary and then generates the channel. Otherwise it simply generates a channel from the cached instance of the factory.

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;
}

I've stripped this example down some from what I use at work. There's a lot you can do in this method - you can handle multiple bindings, assign credentials for authentication, etc. Its pretty much your one stop shopping center for generating a client.

Finally, when I use it in the application, I generally create a channel, do my business, and close it (or abort it if need be). For example:

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);
}

Hopefully the above examples will give you something to go on. I've been using something similar to this for about a year now in a production environment and it's worked very well. 99% of any problems we've encountered have usually been related to something outside the application (either external clients or data sources not under our direct control).

Let me know if anything isn't clear or you have further questions.

一身仙ぐ女味 2024-12-17 19:22:33

您始终可以将每个 WCF 合约的 ChannelFactory 设为静态...

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

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

You could always just make your ChannelFactory static for each WCF Contract...

You should be aware that from .Net 3.5 the proxy objects are pooled for performance reasons by the channel factory. Calling the ICommunicationObject.Close() method actually returns the object to the pool in the hope it can be reused.

I would look at the profiler if you want to do some optimisation, if you can prevent just one IO call being made in your code it could far outweigh any optimisation you will make with the channel factory. Don't pick an area to optimise, use the profiler to find where you can target an optimisation. If you have an SQL database for instance, you will probably find some low hanging fruit in your queries that will get you orders of magnitude performance increases if these haven't already been optimised.

毁梦 2024-12-17 19:22:33

创建通道会降低性能。实际上,如果你在客户端使用ClientBase而不是纯粹的ChannelFactory,WCF已经有了ChannelFactory的缓存机制。但是,如果您进行某些操作,缓存就会过期(如果您愿意,请google一下以获取详细信息)。
对于 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方法的逻辑和参数。但这一启动器类不仅限于一个端点。它对于应用程序中的所有端点都很强大。希望。它很适合你。顺便提一句。这个解决方案不是我提供的。我从一本书上得到这个。

Creating the Channel costs the performance so much. actually , WCF already has the cache mechanism for the ChannelFactory if you use the ClientBase in the client instead of the pure ChannelFactory. But the cache will be expired if you make some anditional operations(Please google it for details if you want).
For the ErOx's issue i got another solution i think it is better. see below:


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 { }
}

you can customize the logic and the parameters of the Initiate method yourself if you got another requirement. but this initiator class is not limited only one endpoint. it is powerful for all of the endpoint in your application. hopefully. it works well for you. BTW. this solution is not from me. i got this from a book.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文