当前位置:  开发笔记 > 编程语言 > 正文

WCF,BasicHttpBinding:停止新连接但允许现有连接继续

如何解决《WCF,BasicHttpBinding:停止新连接但允许现有连接继续》经验,为你挑选了1个好方法。

.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拒绝请求.



1> Benjamin Pod..:

猜测:

您是否尝试在运行时(从端点)获取绑定,将其强制转换为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, Collection endpoints, 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
//所有其​​他消息从未发出它到服务器然后因故障而死
在块之后产生的客户端请求错误.
块后生成的客户端请求出错.
块后生成的客户端请求出错.
块之前的客户端请求出错.
块之前的客户端请求出错.

推荐阅读
Gbom2402851125
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有