WCF、BasicHttpBinding:停止新连接但允许现有连接继续 [英] WCF, BasicHttpBinding: Stop new connections but allow existing connections to continue

查看:17
本文介绍了WCF、BasicHttpBinding:停止新连接但允许现有连接继续的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

.NET 3.5、VS2008、WCF 服务使用 BasicHttpBinding

.NET 3.5, VS2008, WCF service using BasicHttpBinding

我有一个 WCF 服务托管在 Windows 服务中.当 Windows 服务因升级、定期维护等原因关闭时,我需要优雅地关闭我的 WCF 服务.WCF 服务的方法可能需要几秒钟才能完成,典型的数量是每秒 2-5 次方法调用.我需要以某种方式关闭 WCF 服务,以允许任何先前调用的方法完成,同时拒绝任何新调用.通过这种方式,我可以在大约 5-10 秒内进入安静状态,然后完成我的 Windows 服务的关闭周期.

I have a WCF service hosted in a Windows service. When the Windows service shuts down, due to upgrades, scheduled maintenance, etc, I need to gracefully shut down my WCF service. The WCF service has methods that can take up to several seconds to complete, and typical volume is 2-5 method calls per second. I need to shut down the WCF service in a way that allows any previously call methods to complete, while denying any new calls. In this manner, I can reach a quiet state in ~ 5-10 seconds and then complete the shutdown cycle of my Windows service.

调用 ServiceHost.Close 似乎是正确的方法,但它立即关闭客户端连接,无需等待任何正在进行的方法完成.我的 WCF 服务完成了它的方法,但没有人可以发送响应,因为客户端已经断开连接.这是this question建议的解决方案.

Calling ServiceHost.Close seems like the right approach, but it closes client connections right away, without waiting for any methods in progress to complete. My WCF service completes its method, but there is no one to send the response to, because the client has already been disconnected. This is the solution suggested by this question.

这是事件的顺序:

  1. 客户端调用服务方法,使用VS生成的代理类
  2. 服务开始执行服务方法
  3. 服务收到关闭请求
  4. 服务调用 ServiceHost.Close(或 BeginClose)
  5. 客户端已断开连接,并收到 System.ServiceModel.CommunicationException
  6. Service 完成服务方法.
  7. 最终服务检测到它没有更多工作要做(通过应用程序逻辑)并终止.

我需要的是让客户端连接保持打开状态,以便客户端知道他们的服务方法已成功完成.现在他们只是得到一个关闭的连接,不知道服务方法是否成功完成.在使用 WCF 之前,我使用的是套接字并且能够通过直接控制 Socket 来做到这一点.(即停止接受循环,同时仍在执行接收和发送)

What I need is for the client connections to be kept open so the clients know that their service methods completed sucessfully. Right now they just get a closed connection and don't know if the service method completed successfully or not. Prior to using WCF, I was using sockets and was able to do this by controlling the Socket directly. (ie stop the Accept loop while still doing Receive and Send)

关闭主机 HTTP 端口很重要,这样上游防火墙可以将流量定向到另一个主机系统,但现有连接保持打开状态以允许完成现有方法调用.

It is important that the host HTTP port is closed so that the upstream firewall can direct traffic to another host system, but existing connections are left open to allow the existing method calls to complete.

有没有办法在 WCF 中实现这一点?

Is there a way to accomplish this in WCF?

我尝试过的事情:

  1. ServiceHost.Close() - 立即关闭客户端
  2. ServiceHost.ChannelDispatchers - 对每个调用 Listener.Close() - 似乎没有做任何事情
  3. ServiceHost.ChannelDispatchers - 对每个调用 CloseInput() - 立即关闭客户端
  4. 覆盖 ServiceHost.OnClosing() - 让我延迟关闭直到我决定可以关闭,但在此期间允许新连接
  5. 使用此处描述的技术移除端点.这会消灭一切.
  6. 运行网络嗅探器来观察 ServiceHost.Close().主机只是关闭连接,没有发送响应.
  1. ServiceHost.Close() - closes clients right away
  2. ServiceHost.ChannelDispatchers - call Listener.Close() on each - doesn't seem to do anything
  3. ServiceHost.ChannelDispatchers - call CloseInput() on each - closes clients right away
  4. Override ServiceHost.OnClosing() - lets me delay the Close until I decide it is ok to close, but new connections are allowed during this time
  5. Remove the endpoint using the technique described here. This wipes out everything.
  6. Running a network sniffer to observe ServiceHost.Close(). The host just closes the connection, no response is sent.

谢谢

编辑:不幸的是,我无法实现系统正在关闭的应用程序级咨询响应,因为现场的客户端已经部署.(我只控制服务,不控制客户端)

Edit: Unfortunately I cannot implement an application-level advisory response that the system is shutting down, because the clients in the field are already deployed. (I only control the service, not the clients)

编辑:我使用 Redgate Reflector 查看 Microsoft 的 ServiceHost.Close 实现.不幸的是,它调用了一些我的代码无法访问的 internal 辅助类.

Edit: I used the Redgate Reflector to look at Microsoft's implementation of ServiceHost.Close. Unfortunately, it calls some internal helper classes that my code can't access.

编辑:我还没有找到我正在寻找的完整解决方案,但 Benjamin 建议在进入服务方法之前使用 IMessageDispatchInspector 拒绝请求最接近.

Edit: I haven't found the complete solution I was looking for, but Benjamin's suggestion to use the IMessageDispatchInspector to reject requests prior to entering the service method came closest.

推荐答案

猜测:

您是否尝试在运行时(从端点)获取绑定,将其转换为 BasicHttpBinding 并(重新)定义那里的属性?

Have you tried to grab the binding at runtime (from the endpoints), cast it to BasicHttpBinding and (re)define the properties there?

我的最佳猜测:

  • 打开超时
  • MaxReceivedMessageSize
  • 读者配额

这些可以根据文档在运行时设置,并且似乎允许所需的行为(阻止新客户端).不过,这对上游防火墙/负载平衡器需要重新路由"部分没有帮助.

Those can be set at runtime according to the documentation and seem to allow the desired behaviour (blocking new clients). This wouldn't help with the "upstream firewall/load balancer needs to reroute" part though.

最后的猜测:你能(文档说是的,但我不确定后果是什么)根据需要将端点的地址重新定义为本地主机地址吗?这也可以作为防火墙主机的端口关闭",如果它无论如何都不会杀死所有客户端..

Last guess: Can you (the documention says yes, but I'm not sure what the consequences are) redefine the address of the endpoint(s) to a localhost address on demand? This might work as a "Port close" for the firewall host as well, if it doesn't kill of all clients anyway..

在使用上述建议和有限测试时,我开始使用目前看起来很有希望的消息检查器/行为组合:

While playing with the suggestions above and a limited test I started playing with a message inspector/behavior combination that looks promising for now:

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<ServiceEndpoint> 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 内部的随机数),如下所示:

Forget about the crappy lock usage etc., but using this with a very simple WCF test (returning a random number with a Thread.Sleep inside) like this:

var sh = new ServiceHost(new WCFTestService(), baseAdresses);

var filter = new WCFFilter();
sh.Description.Behaviors.Add(filter);

然后翻转 BlockRequests 属性,我得到以下行为(再次:这当然是一个非常非常简化的示例,但我希望它无论如何都可以为您工作):

and later flipping the BlockRequests property I get the following behavior (again: This is of course a very, very simplified example, but I hope it might work for you anyway):

//我生成了 3 个线程请求号码..
请求号码..
请求号码..
//一个传入请求的服务器端日志
对号码的传入请求.
//主循环翻转阻止一切" bool
从这里开始阻止访问.
//之后又增加了 3 个客户端,这是很好的衡量标准
请求号码..
请求号码..
请求号码..
//第一个请求(带有服务器端日志,见上文)成功完成
收到 1569129641
//所有其他消息还没有到达服务器并因故障而死亡
块后产生的客户端请求错误.
块后产生的客户端请求错误.
块之后产生的客户端请求错误.
块前客户端请求出错.
块前客户端请求出错.

// I spawn 3 threads Requesting a number..
Requesting a number..
Requesting a number..
// Server side log for one incoming request
Incoming request for a number.
// Main loop flips the "block everything" bool
Blocking access from here on.
// 3 more clients after that, for good measure
Requesting a number..
Requesting a number..
Requesting a number..
// First request (with server side log, see above) completes sucessfully
Received 1569129641
// All other messages never made it to the server yet and die with a fault
Error in client request spawned after the block.
Error in client request spawned after the block.
Error in client request spawned after the block.
Error in client request before the block.
Error in client request before the block.

这篇关于WCF、BasicHttpBinding:停止新连接但允许现有连接继续的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆