.NET 3.5,VS2008,使用BasicHttpBinding的WCF服务
我有一个Windows服务托管的WCF服务.当Windows服务因升级,定期维护等原因关闭时,我需要正常关闭我的WCF服务.WCF服务的方法可能需要几秒钟才能完成,而典型的卷每秒会有2-5个方法调用.我需要以允许任何先前调用方法完成的方式关闭WCF服务,同时拒绝任何新调用.通过这种方式,我可以在约5-10秒内达到安静状态,然后完成Windows服务的关闭周期.
调用ServiceHost.Close似乎是正确的方法,但它会立即关闭客户端连接,而无需等待任何正在进行的方法完成.我的WCF服务完成了它的方法,但没有人发送响应,因为客户端已经断开连接.这是此问题提出的解决方案.
以下是事件序列:
客户端使用VS生成的代理类调用服务方法
服务开始执行服务方法
服务收到关闭请求
服务调用ServiceHost.Close(或BeginClose)
客户端已断开连接,并收到System.ServiceModel.CommunicationException
服务完成服务方法.
最终服务检测到它没有更多的工作要做(通过应用程序逻辑)并终止.
我需要的是让客户端连接保持打开,以便客户知道他们的服务方法成功完成.现在他们只是获得了一个封闭的连接,并且不知道服务方法是否成功完成.在使用WCF之前,我使用套接字并且能够通过直接控制Socket来实现.(即在仍然执行接收和发送时停止接受循环)
关闭主机HTTP端口非常重要,以便上游防火墙可以将流量定向到另一个主机系统,但现有连接保持打开状态以允许现有方法调用完成.
有没有办法在WCF中实现这一目标?
我尝试过的事情:
ServiceHost.Close() - 立即关闭客户端
ServiceHost.ChannelDispatchers - 在每个上调用Listener.Close() - 似乎什么都不做
ServiceHost.ChannelDispatchers - 在每个上调用CloseInput() - 立即关闭客户端
覆盖ServiceHost.OnClosing() - 让我延迟关闭,直到我决定关闭,但在此期间允许新的连接
使用此处描述的技术删除端点.这抹去了一切.
运行网络嗅探器以观察ServiceHost.Close().主机只关闭连接,不发送任何响应.
谢谢
编辑:不幸的是,我无法实现系统正在关闭的应用程序级别的建议响应,因为该领域的客户端已经部署.(我只控制服务,而不是客户端)
编辑:我使用Redgate Reflector来查看Microsoft的ServiceHost.Close实现.不幸的是,它调用了internal
我的代码无法访问的一些辅助类.
编辑:我还没有找到我想要的完整解决方案,但Benjamin建议在输入服务方法之前使用IMessageDispatchInspector拒绝请求.
猜测:
您是否尝试在运行时(从端点)获取绑定,将其强制转换为BasicHttpBinding并在其中(重新)定义属性?
我最好的猜测:
OpenTimeout
MaxReceivedMessageSize
ReaderQuotas
这些可以根据文档在运行时设置,并且似乎允许所需的行为(阻止新客户端).但这对"上游防火墙/负载均衡器需要重新路由"部分没有帮助.
最后一个猜测:你可以(文件说是,但我不确定后果是什么)根据需要将端点的地址重新定义为本地主机地址?如果它不会杀死所有客户端,它也可以作为防火墙主机的"端口关闭".
编辑:在玩上面的建议和有限的测试时,我开始玩消息检查器/行为组合,现在看起来很有希望:
public class WCFFilter : IServiceBehavior, IDispatchMessageInspector { private readonly object blockLock = new object(); private bool blockCalls = false; public bool BlockRequests { get { lock (blockLock) { return blockCalls; } } set { lock (blockLock) { blockCalls = !blockCalls; } } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collectionendpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) { foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this); } } } public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { lock (blockLock) { if (blockCalls) request.Close(); } return null; } public void BeforeSendReply(ref Message reply, object correlationState) { } }
忘记糟糕的锁用法等,但是使用这个非常简单的WCF测试(返回一个带有Thread.Sleep的随机数),如下所示:
var sh = new ServiceHost(new WCFTestService(), baseAdresses); var filter = new WCFFilter(); sh.Description.Behaviors.Add(filter);
然后翻转BlockRequests属性我得到以下行为(再次:这当然是一个非常非常简单的例子,但我希望它可能对你有用):
//我产生3个线程请求一个号码..
请求一个号码..
请求一个号码..
//一个传入请求的服务器端日志
一个号码的传入请求.
//主循环翻转"阻止所有内容"bool
从此处阻止访问.
//之后有3个以上的客户端,为了好的措施
请求号码..
请求号码..
请求号码..
//第一个请求(带服务器端日志,见上文)完成成功
收到1569129641
//所有其他消息从未发出它到服务器然后因故障而死
在块之后产生的客户端请求错误.
块后生成的客户端请求出错.
块后生成的客户端请求出错.
块之前的客户端请求出错.
块之前的客户端请求出错.