WCF代理客户端创建时间过长,是否有缓存或单例解决方案?

7

我们有超过十二个WCF服务,使用TCP绑定进行调用。在代码的不同位置有很多对同一WCF服务的调用。

AdminServiceClient client = FactoryS.AdminServiceClient();// it takes significant time. and 

client.GetSomeThing(param1);
client.Close();

我想要缓存客户端或从单例中生成它,这样可以节省一些时间,这种做法可行吗?
谢谢。

使用代理的应用程序是智能客户端还是Web应用程序? - Richard Blewett
2个回答

8
是的,这是可能的。您可以使代理对象对整个应用程序可见,或将其包装在单例类中以保持整洁(我更喜欢的选项)。但是,如果要重复使用服务的代理,则必须处理通道故障。
首先创建单例类/缓存/全局变量,其中包含您想要重复使用的代理实例(或代理实例)。
创建代理时,需要订阅内部通道上的 "Faulted" 事件。
proxyInstance.InnerChannel.Faulted += new EventHandler(ProxyFaulted);

然后在ProxyFaulted事件处理程序中放置一些重新连接的代码。如果服务掉线或连接因空闲而超时,则会触发故障事件。如果在配置文件中启用了可靠会话(如果未指定,则默认在netTcpBinding上启用),则只会触发故障事件。
编辑:如果您不想一直保持代理通道处于打开状态,则必须在每次使用之前测试通道的状态,并在其出现故障时重新创建代理。一旦通道出现故障,就没有其他选择,只能创建新通道。
编辑2:保持通道打开和每次关闭之间唯一的真正区别是会定期发送保持活动数据包到服务并进行确认(这就是通道故障事件背后的原因)。我认为对于100个用户来说,这不会成为问题。
另一种选择是将代理创建放在using块中,在块的末尾关闭/处理它(这被认为是不良实践considered bad practice)。在调用后关闭通道可能会导致应用程序挂起,因为服务尚未完成处理。事实上,即使您对服务的调用是异步的或方法的服务契约是单向的,通道关闭代码也会阻塞,直到服务完成。
这里是一个简单的单例类,应该具备您所需的基本功能:
public static class SingletonProxy
{
    private CupidClientServiceClient proxyInstance = null;
    public CupidClientServiceClient ProxyInstance
    {
        get
        {
            if (proxyInstance == null)
            {
                AttemptToConnect();
            }
            return this.proxyInstance;
        }
    }

    private void ProxyChannelFaulted(object sender, EventArgs e)
    {
        bool connected = false;
        while (!connected)
        {
            // you may want to put timer code around this, or 
            // other code to limit the number of retrys if 
            // the connection keeps failing
            AttemptToConnect();
        }
    }

    public bool AttemptToConnect()
    {
        // this whole process needs to be thread safe
        lock (proxyInstance)
        {
            try
            {
                if (proxyInstance != null)
                {
                    // deregister the event handler from the old instance
                    proxyInstance.InnerChannel.Faulted -= new EventHandler(ProxyChannelFaulted);
                }

                //(re)create the instance
                proxyInstance = new CupidClientServiceClient();
                // always open the connection
                proxyInstance.Open();

                // add the event handler for the new instance
                // the client faulted is needed to be inserted here (after the open)
                // because we don't want the service instance to keep faulting (throwing faulted event)
                // as soon as the open function call.
                proxyInstance.InnerChannel.Faulted += new EventHandler(ProxyChannelFaulted);

                return true;
            }
            catch (EndpointNotFoundException)
            {
                // do something here (log, show user message etc.)
                return false;
            }
            catch (TimeoutException)
            {
                // do something here (log, show user message etc.)
                return false;
            }
        }
    }
}

我希望这有所帮助 :)


@Franchesca 如果您能够通过代码/参考资料进行更详细的解释,那就更好了。当频道始终开放时与关闭频道时存在哪些优缺点,因为将有100个用户访问该网站。谢谢。 - Techmaster
如果没有更多关于你当前架构的细节,很难给出一个好的答案。你是有1000个用户同时使用1000个客户端应用程序并调用WCF服务,还是有1000个用户同时接入单一实例的客户端应用程序(然后再调用WCF服务)? - Franchesca
创建WCF服务实例时是否存在很大的开销?perCall模式意味着每次从代理调用服务时都会创建一个新的服务实例。这可能是为什么事情有点慢的原因。尝试将您的服务更改为perSession模式(这样您就可以重复使用同一个服务实例进行多个调用)。 - Franchesca
@EkoostikMartin 你是什么意思?你只是从互联网上复制粘贴代码到你的项目中,然后期望它能正常工作吗?这不足以给出-1的理由。如果你需要帮助,请提出问题 :) - Franchesca
@Franchesca - 不,我没有复制粘贴代码,但是有很多错误(不同种类的),你应该把它标记为伪代码。 - EkoostikMartin
显示剩余5条评论

2
根据我的经验,在每个调用中创建/关闭通道几乎不会增加额外的开销。请查看 这个Stackoverflow问题。它本身不是一个单例问题,但与您的问题相关。通常情况下,你不想在使用完后保留通道开启状态。
如果您尚未使用可重用的ChannelFactory实现,我建议您使用它,并查看是否仍然存在性能问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接