我有一个Web服务,我在三台负载均衡的Web服务器上运行,而且我遇到了零星的错误。现在,我承认负载平衡部分可能有点红鲱鱼,但是当我只用1个Web服务器进行测试时,我无法重现错误。如果我测试所有三个Web服务器,我可以得到错误(但它不是100%的时间,更像是50%)。所有测试都是通过负载均衡器完成的,我们只告诉负载均衡器我们想要多少台服务器。
代码是简单的单一请求代码。也就是说,没有国家。发出请求并返回响应。 Web服务代码是在IIS 7.5上运行的c#.NET 4。客户端代码既是网站也是桌面应用程序。
我得到两个例外之一:
System.ServiceModel.Security.MessageSecurityException: 不安全或不正确的安全 从另一方收到了错误 派对。查看内部FaultException 用于故障代码和细节。 ---> System.ServiceModel.FaultException: 安全上下文令牌已过期 或无效。消息不是 处理。
或者我得到:
System.ServiceModel.Security.SecurityNegotiationException: 安全通道无法打开 因为与安全谈判 远程端点失败。这可能 由于缺席或不正确 指定的EndpointIdentity 用于创建的EndpointAddress 渠道。请验证 指定或暗示的EndpointIdentity 由EndpointAddress正确 标识远程端点。 ---> System.ServiceModel.FaultException: 安全令牌的请求有 无效或格式错误的元素。
从我的.config文件中可以看到以下剪辑,我没有使用安全性,因为这严格来说是一个内部Web服务。 (名称已被更改以保护无辜者 - 即我)。
服务器端:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Service Side web.config -->
...
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service behaviorConfiguration="InternalUseOnly.InternalUseOnlyServiceBehavior" name="InternalUseOnly.InternalUseOnlyService">
<endpoint address="" bindingNamespace="http://somecompany.com/webservices" binding="wsHttpBinding" contract="InternalUseOnly.IInternalUseOnlyService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="InternalUseOnly.InternalUseOnlyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
...
</configuration>
客户端
<?xml version="1.0" encoding="UTF-8"?>
<!-- Client Side web.config -->
<configuration>
...
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IInternalUseOnlyService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://intranet.somecompany.com/InternalUseOnly/InternalUseOnlyService.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IInternalUseOnlyService" contract="InternalUseOnlyService.IInternalUseOnlyService" name="WSHttpBinding_IInternalUseOnlyService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
...
</configuration>
有人想到吗?
其他信息:在查看下面的答案后,我尝试了两件事,但都没有成功。
最明显的变化(我最初没有注意到)是更改客户端上的一个属性以允许cookie <system.serviceModel><bindings><wsHttpBinding><binding name="blah, blah, blah" ... other properties... allowCookies="true" />
默认为false。此外,我们的负载均衡器使用cookie来保持亲和力。但是,它没有什么区别(不知道为什么)。
接下来,我在客户端app.config文件中尝试了各种安全选项。这包括<security mode="None" />
和更详细的内容:
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" />
<message clientCredentialType="None" establishSecurityContext="false" negotiateServiceCredential="false"/>
</security>
尽管最后一个设置只是我的猜测。我没有对app.config进行任何服务器端更改,因为我不知道要改变什么,遗憾的是,我只能测试生产,因为我们只有1个开发Web服务器,而不是3个。
答案 0 :(得分:4)
我将在这里走出困境并猜测所涉及的安全性是客户端指定的Message安全性:
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" />
</security>
如果您正在创建客户端并进行连接,则可以缓存协商的Windows凭证令牌。如果您没有启用粘性会话,则令牌可能会传递回错误的服务器并将失败。我的猜测是它总是在第二次通话?
答案 1 :(得分:2)
使用没有粘性会话的负载均衡器导致NTLM问题。要纠正此问题,您需要配置会话关联(粘性会话)。如果不这样做,则会出现故障,因为部分NTLM握手发生在一台服务器上而另一部分发生在另一台服务器上。
答案 2 :(得分:1)
虽然克里斯和杰夫帮助让我顺利回答,但实际为我解决的是我在微软Load Balancing Web Services发现的这篇文章。简而言之,我们为Web场解决此问题所要做的就是从默认的wsHttpBinding
切换到basicHttpBinding
。这并不困难,但这是一个全有或全无的举动。主要的Web服务和每个客户端必须同时重新配置,否则它将会中断。
虽然wsHttpBinding
确实具有allowCooikes属性可以设置为true,但是在建立连接之前它显然不会使用它们,此时请求可以在第一个请求上跳转服务器,从而失败