gpt4 book ai didi

asp.net-mvc - 处置 WCF 代理的正确方法是什么?

转载 作者:行者123 更新时间:2023-12-04 12:58:12 33 4
gpt4 key购买 nike

我一直在努力使用 WCF 代理。处置 WCF 代理的正确方法是什么?答案并非微不足道。

System.ServiceModel.ClientBase 违反了微软自己的 Dispose-pattern
System.ServiceModel.ClientBase<TChannel>确实实现 IDisposable所以必须假设它应该在 using 中处理或使用-堵塞。这些是任何一次性用品的最佳实践。然而,实现是显式的,因此必须显式地强制转换 ClientBase实例到 IDisposable ,使问题变得模糊。

然而,最大的困惑来源是拨打 Dispose()ClientBase发生故障的实例,即使是因为从未打开而发生故障的 channel ,也会导致抛出异常。这不可避免地意味着,当堆栈展开时,解释错误的有意义的异常立即丢失,using范围结束和 Dispose()抛出一个无意义的异常,说你不能处理一个有故障的 channel 。

上述行为是对 dispose 模式的诅咒,该模式规定对象必须容忍对 Dispose() 的多次显式调用。 . (请参阅 http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx ,“...允许多次调用 Dispose(bool) 方法。该方法可能会在第一次调用后选择不执行任何操作。”)

随着控制反转的出现,这种糟糕的实现成为一个真正的问题。国际奥委会容器(特别是 Ninject)检测到 IDisposable接口(interface)和调用Dispose()在注入(inject)范围结束时显式地在激活的实例上。

解决方案:代理 ClientBase 和 Intercept 调用 Dispose()

我的解决方案是代理 ClientBase通过子类化 System.Runtime.Remoting.Proxies.RealProxy并劫持或拦截对 Dispose() 的调用.我的第一个替换 Dispose()是这样的:

if (_client.State == CommunicationState.Faulted) _client.Abort();
else ((IDisposable)_client).Dispose();

(请注意, _client 是对代理目标的类型化引用。)

NetTcpBinding 的问题

最初我以为这已经解决了,但后来我在生产中发现了一个问题:在某些难以重现的场景下,我发现 channel 使用 NetTcpBinding在无故障的情况下没有正确关闭,即使 Dispose被叫到 _client .

我有一个 ASP.NET MVC 应用程序,它使用我的代理实现来使用 NetTcpBinding 连接到 WCF 服务。在本地网络上,托管在只有一个节点的服务集群上的 Windows NT 服务中。当我对 MVC 应用程序进行负载测试时,WCF 服务(使用端口共享)上的某些端点会在一段时间后停止响应。

我努力重现这一点:在两台开发人员的机器之间的 LAN 上运行的相同组件运行良好;一个控制台应用程序锤击真正的 WCF 端点(在登台服务集群上运行),每个进程都有许多进程和线程;在登台服务器上配置 MVC 应用程序以连接到在负载下工作的开发人员机器上的端点;在开发人员的机器上运行 MVC 应用程序并连接到暂存 WCF 端点。然而,最后一个场景仅适用于 IIS Express,这是一个突破。在开发人员的机器上在全脂 IIS 下对 MVC 应用程序进行负载测试时,端点会卡住,连接到登台服务集群。

解决方法:关闭 channel

在未能理解问题并阅读了许多声称该问题根本不存在的 MSDN 和其他来源的许多页面后,我尝试了一个长期尝试并更改了我的 Dispose()解决...
if (_client.State == CommunicationState.Faulted) _client.Abort();
else if (_client.State == CommunicationState.Opened)
{
((IContextChannel)_client.Channel).Close();
((IDisposable)_client).Dispose();
}
else ((IDisposable)_client).Dispose();

...并且问题不再出现在所有测试设置中,并且在登台环境中处于负载状态!

为什么?

谁能解释一下可能发生了什么以及为什么明确关闭 Channel调用前 Dispose()解决了吗?据我所知,这应该没有必要。

最后,我回到开头的问题:处置 WCF 代理的正确方法是什么?是我的替代品 Dispose()足够的?

最佳答案

据我所知,问题是拨打 Dispose处理掉句柄,但实际上并没有关闭 channel ,然后 channel 会保留资源并最终超时。

这就是为什么您的服务在负载测试期间一段时间后停止响应的原因:因为最初的调用占用资源的时间比您想象的要长,后来的调用无法使用这些资源。

我想出了以下解决方案。解决的前提是拨打Dispose应该足以处理掉 handle 并关闭 channel 。额外的好处是,如果客户端最终处于故障状态,则会重新创建它,以便后续调用成功。

ServiceClient<TService>通过像 Ninject 这样的依赖注入(inject)框架注入(inject)到另一个类中,然后所有资源将被正确释放。

注意:请注意在 Ninject 的情况下,绑定(bind)必须定义一个范围,即它不能缺少 InXyzScope或者用 InTransientScope 定义.如果没有范围有意义,则使用 InCallScope .

这是我想出的:

public class ServiceClient<TService> : IDisposable
{
private readonly ChannelFactory<TService> channelFactory;

private readonly Func<TService> createChannel;

private Lazy<TService> service;

public ServiceClient(ChannelFactory<TService> channelFactory)
: base()
{
this.channelFactory = channelFactory;

this.createChannel = () =>
{
var channel = ChannelFactory.CreateChannel();

return channel;
};

this.service = new Lazy<TService>(() => CreateChannel());
}

protected ChannelFactory<TService> ChannelFactory
{
get
{
return this.channelFactory;
}
}

protected Func<TService, bool> IsChannelFaulted
{
get
{
return (service) =>
{
var channel = service as ICommunicationObject;

if (channel == null)
{
return false;
}

return channel.State == CommunicationState.Faulted;
};
}
}

protected Func<TService> CreateChannel
{
get
{
return this.createChannel;
}
}

protected Action<TService> DisposeChannel
{
get
{
return (service) =>
{
var channel = service as ICommunicationObject;

if (channel != null)
{
switch (channel.State)
{
case CommunicationState.Faulted:
channel.Abort();
break;

case CommunicationState.Closed:
break;

default:
try
{
channel.Close();
}
catch (CommunicationException)
{
}
catch (TimeoutException)
{
}
finally
{
if (channel.State != CommunicationState.Closed)
{
channel.Abort();
}
}
break;
}
}
};
}
}

protected Action<ChannelFactory<TService>> DisposeChannelFactory
{
get
{
return (channelFactory) =>
{
var disposable = channelFactory as IDisposable;

if (disposable != null)
{
disposable.Dispose();
}
};
}
}

public void Dispose()
{
DisposeChannel(this.service.Value);
DisposeChannelFactory(this.channelFactory);
}

public TService Service
{
get
{
if (this.service.IsValueCreated && IsChannelFaulted(this.service.Value))
{
DisposeChannel(this.service.Value);

this.service = new Lazy<TService>(() => CreateChannel());
}

return this.service.Value;
}
}
}

关于asp.net-mvc - 处置 WCF 代理的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27441362/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com