【问题标题】:Diagnosing "RPC Server Unavailable Error" which is cause by Windows Service calling WCF诊断由 Windows 服务调用 WCF 引起的“RPC 服务器不可用错误”
【发布时间】:2012-03-19 14:44:27
【问题描述】:

我有一个使用 WCF 连接到其他服务的 Windows 服务。它检查它们是否处于活动状态,获取这些服务具有的任何错误消息,并报告这些错误消息。使用通道工厂每 30 秒检查一次,其中为在符合接口的配置中找到的每个服务创建代理。在正常运行几天后,服务器变得无响应并开始报告“RPC 服务器不可用错误”。我可以使用计算机管理来连接它,它的内存足迹似乎没有增加,但如果我停止服务,它会完全解决问题。我已经附上了我正在使用的渠道工厂经理,但如果需要其他任何东西,请告诉我。难道是服务通道没有被正确释放?我能做些什么来诊断这个?有没有人遇到过这种情况?

public class ChannelFactoryManager : IDisposable
{
    private static Dictionary<Tuple<Type, string>, ChannelFactory> _factories = new Dictionary<Tuple<Type, string>, ChannelFactory>();
    private static readonly object _syncRoot = new object();

    public virtual T CreateChannel<T>() where T : class
    {
        return CreateChannel<T>("*", null);
    }

    public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class
    {
        return CreateChannel<T>(endpointConfigurationName, null);
    }

    public virtual T CreateChannel<T>(string endpointConfigurationName, string endpointAddress) where T : class
    {
        T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel();
        ((IClientChannel)local).Faulted += ChannelFaulted;
        return local;
    }

    protected virtual ChannelFactory<T> GetFactory<T>(string endpointConfigurationName, string endpointAddress) where T : class
    {
        lock (_syncRoot)
        {
            ChannelFactory factory;
            if (!_factories.TryGetValue(new Tuple<Type, string>(typeof(T), endpointConfigurationName), out factory))
            {
                factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress);
                _factories.Add(new Tuple<Type, string>(typeof(T), endpointConfigurationName), factory);
            }
            return (factory as ChannelFactory<T>);
        }
    }

    private ChannelFactory CreateFactoryInstance<T>(string endpointConfigurationName, string endpointAddress)
    {
        ChannelFactory factory = null;
        if (!string.IsNullOrEmpty(endpointAddress))
        {
            factory = new ChannelFactory<T>(endpointConfigurationName, new EndpointAddress(endpointAddress));
        }
        else
        {
            factory = new ChannelFactory<T>(endpointConfigurationName);
        }
        factory.Faulted += FactoryFaulted;
        factory.Open();
        return factory;
    }

    private void ChannelFaulted(object sender, EventArgs e)
    {
        IClientChannel channel = (IClientChannel)sender;
        channel.Abort();
    }

    private void FactoryFaulted(object sender, EventArgs args)
    {
        ChannelFactory factory = (ChannelFactory)sender;
        factory.Abort();            

        Type[] genericArguments = factory.GetType().GetGenericArguments();
        if ((genericArguments != null) && (genericArguments.Length == 1))
        {
            Type type = genericArguments[0];
            string endPointName = factory.Endpoint.Name;
            Tuple<Type, string> key = new Tuple<Type, string>(type, endPointName);

            if (_factories.ContainsKey(key))
            {
                _factories.Remove(key);
            }
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (_syncRoot)
            {
                foreach (Tuple<Type, string> type in _factories.Keys)
                {
                    ChannelFactory factory = _factories[type];
                    try
                    {
                        factory.Close();
                        continue;
                    }
                    catch
                    {
                        factory.Abort();
                        continue;
                    }
                }
                _factories.Clear();
            }
        }
    }
}

谢谢 抢

【问题讨论】:

  • 您所描述的问题听起来确实像您的处置代码没有按预期工作。如果您想删除此Gordian Knot,请摆脱静态集合,只需在您选择的良好 WCF 客户端 try/catch 模式(create>try>ca​​ll>close>catch>abort)中根据需要实例化每个服务代理。性能会受到影响,但至少你可以指望不会像现在这样泄露网络资源:)
  • 好的,谢谢,我会试一试。性能应该不是什么大问题,所以也许这样的事情会是更好的选择。

标签: c# wcf rpc diagnostics


【解决方案1】:

如果您根据需要使用实例化服务代理,SO question 中的答案会提供一些选项和处理代理实例的基本原理。作为一个基本的开始,我建议:

//Your client type could be ICommunicationObject or ClientBase:
var client = new YourServiceProxyType();
try {
    var result = client.MakeCall();
    //do stuff with result...

    //Done with client. Close it:
    client.Close();
}
catch (Exception ex) {
    if (client.State != System.ServiceModel.CommunicationState.Closed)
        client.Abort();
}

设计一个好的 WCF 代理处理模式的根本问题是,微软的 WCF 团队决定以一种可以抛出异常的方式实现 Dispose,从而防止释放非托管资源,直到调用 Abort() 或代理实例完全垃圾收集。他们编写了框架,以便他们做出选择,不幸的是我们不得不承担后果。

【讨论】:

  • 谢谢我已经这样做了,并将其标记为正确答案。我想在某个时候为 IoC 和单元测试内容抽象出来,至少我知道问题在于处置并且可以解决它。
猜你喜欢
  • 1970-01-01
  • 2013-01-07
  • 2010-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
相关资源
最近更新 更多