【发布时间】:2017-10-20 00:33:52
【问题描述】:
我们有一个 C# 编码的 Windows 服务,它通过向 Web 服务端点发送 HTTP 消息来处理事务。系统在负载下显示 C# 代码发送请求和 Wireshark 接收请求之间的延迟为 1 到 32+ 秒,两者都在同一个虚拟机 (VM) 上。由于 C# Windows 服务和 Wireshark 在同一个 VM 上,因此不涉及网络。当我们在 C# 代码中中止连接时,超时时间(32 秒)过去后,请求最终奇怪地发送到 Web 服务端点并由其接收。我们认为这个问题不是由硬件资源引起的,因为这种情况发生在多个环境中,包括功能强大的生产系统,其中延迟时间相对较低。
我们正在使用 .NET Framework v4.6.1。虚拟机是 Windows 2008 R2,SP1。
问题: 是什么导致了延迟?我们如何消除延迟?
技术细节: 这是 app.config 文件中用于通信的 ServiceModel 原始配置:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ITransaction">
<security mode="Transport"/>
</binding>
<binding name="BasicHttpBinding_ITransaction1"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://.../" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITransaction" contract="PaymentSwitchReference.ITransaction" name="BasicHttpBinding_ITransaction"/>
</client>
在c#代码中,我们创建对象:
_client = new TransactionClient(); //TransactionClient class, proxy which contents the delegates and asynchronous calls to server
_client.ChargeAsync(req); //Call to begin request sending
//In TransactionClient class:
public Plpm.Csp.PaymentService.PaymentSwitchReference.ResponseCharge Charge(Plpm.Csp.PaymentService.PaymentSwitchReference.Charge req) {
return base.Channel.Charge(req);
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public System.IAsyncResult BeginCharge(Plpm.Csp.PaymentService.PaymentSwitchReference.Charge req, System.AsyncCallback callback, object asyncState) { //Here is calling the service
return base.Channel.BeginCharge(req, callback, asyncState);
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public Plpm.Csp.PaymentService.PaymentSwitchReference.ResponseCharge EndCharge(System.IAsyncResult result) {
return base.Channel.EndCharge(result);
}
private System.IAsyncResult OnBeginCharge(object[] inValues, System.AsyncCallback callback, object asyncState) {
Plpm.Csp.PaymentService.PaymentSwitchReference.Charge req = ((Plpm.Csp.PaymentService.PaymentSwitchReference.Charge)(inValues[0]));
return this.BeginCharge(req, callback, asyncState);
}
private object[] OnEndCharge(System.IAsyncResult result) {
Plpm.Csp.PaymentService.PaymentSwitchReference.ResponseCharge retVal = this.EndCharge(result);
return new object[] {
retVal};
}
private void OnChargeCompleted(object state) {
if ((this.ChargeCompleted != null)) {
InvokeAsyncCompletedEventArgs e = ((InvokeAsyncCompletedEventArgs)(state));
this.ChargeCompleted(this, new ChargeCompletedEventArgs(e.Results, e.Error, e.Cancelled, e.UserState));
}
}
public void ChargeAsync(Plpm.Csp.PaymentService.PaymentSwitchReference.Charge req) {
this.ChargeAsync(req, null);
}
public void ChargeAsync(Plpm.Csp.PaymentService.PaymentSwitchReference.Charge req, object userState) { //Creates the delegates and invoke asynchronous communication.
if ((this.onBeginChargeDelegate == null)) {
this.onBeginChargeDelegate = new BeginOperationDelegate(this.OnBeginCharge);
}
if ((this.onEndChargeDelegate == null)) {
this.onEndChargeDelegate = new EndOperationDelegate(this.OnEndCharge);
}
if ((this.onChargeCompletedDelegate == null)) {
this.onChargeCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnChargeCompleted);
}
base.InvokeAsync(this.onBeginChargeDelegate, new object[] {
req}, this.onEndChargeDelegate, this.onChargeCompletedDelegate, userState);
}
当测试运行并且请求数量开始增长时,我们可以看到 BeginCharge 和通过同一 VM 上的 Wireshark 发送事务之间的时间延迟不断增加。我们最终中止了连接(32 秒后),并且我们从 OnChargeCompleted 委托收到了以下预期异常:
> e
> {Plpm.Csp.PaymentService.PaymentSwitchReference.ChargeCompletedEventArgs}
> Cancelled: false
> Error: {"An error (The request was aborted: The request was canceled.) occurred while transmitting data over the HTTP channel."}
> Result: 'e.Result' threw an exception of type 'System.Reflection.TargetInvocationException'
> UserState: null
> cancelled: false
> error: {"An error (The request was aborted: The request was canceled.) occurred while transmitting data over the HTTP channel."}
> results: null
> userState: null
此超时中止导致我们的服务完成并显示此异常消息。但是,在我们的服务中止连接的同时,请求却奇怪地成功发送到另一个 VM 上的 Web 服务端点。我们可以在 Wireshark 中看到它是如何在中止后发送和到达的。
我们尝试通过添加 system.web 配置以及 transferMode 和 Timeouts 参数来更改配置文件如下:
<system.web>
<httpRuntime maxRequestLength="10240"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ITransaction" transferMode="StreamedRequest" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00">
<security mode="None"/>
</binding>
<binding name="BasicHttpBinding_ITransaction1"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://.../" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITransaction" contract="PaymentSwitchReference.ITransaction" name="BasicHttpBinding_ITransaction"/>
</client>
</system.serviceModel>
在 C# 代码中,我们尝试添加以下 KeepAlive 参数:
var customBinding = new CustomBinding(_client.Endpoint.Binding);
var transportElement = customBinding.Elements.Find<HttpTransportBindingElement>();
transportElement.KeepAliveEnabled = false;
_client.Endpoint.Binding = customBinding;
_client.ChargeAsync(req);
但是,结果是一样的,我们继续看到延迟。
任何帮助将不胜感激。
【问题讨论】:
标签: c# .net wcf network-programming tcp-ip