英文:
Upgrade Net Framework 4.7.1 to Net 6 makes WCF service consumption slower
问题
We are rewriting our .Net Framework 4.7.1 backend to .Net 6 and we have a first version. But when performing performance test in local we observed that with same configuration the queries are slower with the new backend. This occurs when consuming data from WCF service: before it took an average of 200 ms, now it is 650 ms.
We had the connected services configured in our web.config.
Now configuration is loaded from appsettings.json in an abstract factory and its implementation.
Here is ConnectedService.json generated in .NET 6 from a local WSDL file.
I have updated to the latest version of System.ServiceModel packages (6.0.0). In the previous backend, it was version 4.0.0. I have used svcUtil version 2.1.0.
I have compared both service object instances and I cannot see any differences that could explain the slow performance.
I have also tried to change a few settings and to use other bindings (NetTcpBinding and WSHttpBinding) without success.
We work only on the client side of the WCF service.
Thank you in advance for your help.
EDIT: I have improved new times to 500 ms thanks to this change GitHub link.
英文:
We are rewriting our .Net Framework 4.7.1 backend to .Net 6 and we have a first version. But when performing performance test in local we observed that with same configuration the queries are slower with the new backend. This occurs when consuming data from WCF service : before it took average 200 ms, now it is 650 ms
We had the connected services configured in our web.config
<system.serviceModel>
<diagnostics performanceCounters="Off">
<messageLogging logMalformedMessages="true" logMessagesAtTransportLevel="true"/>
</diagnostics>
<bindings>
<basicHttpBinding>
<binding name="DocumentSecuredServiceBinding" bypassProxyOnLocal="false" receiveTimeout="00:10:00" sendTimeout="00:10:00" textEncoding="utf-8" messageEncoding="Mtom" maxReceivedMessageSize="209715200" maxBufferPoolSize="5242880" maxBufferSize="209715200">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="DocumentServiceImpl" address="https://service/DocumentSecuredService" binding="basicHttpBinding" bindingConfiguration="DocumentSecuredServiceBinding" behaviorConfiguration="CredentialIdentificationBehavior" contract="DocumentService.DocumentSecuredService"/>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="CredentialIdentificationBehavior">
<clientCredentials>
<clientCertificate findValue="CertName" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
Now configuration is loaded from appsettings.json in an abstract factory and its implementation :
public abstract class BaseSecuredServiceFactory
{
protected readonly X509Certificate2 _clientCertificate;
protected readonly Binding _binding;
protected readonly EndpointAddress _endpointAdress;
protected BaseSecuredServiceFactory(ILoggerFactory loggerFactory, EndpointOptions option)
{
_clientCertificate = LoadCertificate(option.CertificateSubjectName);
_binding = new BasicHttpBinding()
{
BypassProxyOnLocal = option.ByPassProxyOnLocal,
ReceiveTimeout = option.ReceiveTimeout,
SendTimeout = option.SendTimeout,
TextEncoding = Encoding.UTF8,
MessageEncoding = option.MessageEncoding,
MaxReceivedMessageSize = option.MaxReceivedMessageSize,
MaxBufferPoolSize = option.MaxBufferPoolSize,
MaxBufferSize = option.MaxBufferSize,
TransferMode = option.TransferMode,
Security = new BasicHttpSecurity()
{
Mode = option.SecurityMode,
Transport = new HttpTransportSecurity()
{
ClientCredentialType = HttpClientCredentialType.Certificate
}
}
};
_endpointAdress = new EndpointAddress(option.EndpointAdress);
}
protected static X509Certificate2 LoadCertificate(string subjectName)
{
// Load certificate from store My of LocalMachine
X509Store store = new(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = store.Certificates.Find(
X509FindType.FindBySubjectName,
subjectName,
false
);
if (certificates.Count == 0)
{
throw new InvalidOperationException(
$"The certificate with subject name '{subjectName}' could not be found."
);
}
return certificates[0];
}
}
public class DocumentSecuredServiceFactory
: BaseSecuredServiceFactory,
IDocumentSecuredServiceFactory
{
public DocumentSecuredServiceFactory(
ILoggerFactory loggerFactory,
IOptions<DocumentEndpointOptions> options
)
: base(loggerFactory, options.Value) { }
public DocumentSecuredServiceClient GetDocumentSecuredService()
{
DocumentSecuredServiceClient result = new(_binding, _endpointAdress);
result.Endpoint.EndpointBehaviors.Add(new InspectorBehavior(_loggerFactory));
result.ClientCredentials.ClientCertificate.Certificate = _clientCertificate;
return result;
}
}
Configuration section in appsettings.json
// Document Secured Service configuration
{
"ByPassProxyOnLocal": false,
"ReceiveTimeout": "00:10:00",
"SendTimeout": "00:10:00",
"MessageEncoding": "Mtom",
"MaxReceivedMessageSize": 209715200,
"MaxBufferPoolSize": 5242880,
"MaxBufferSize": 209715200,
"TransferMode": 0, // Buffered
"SecurityMode": 1,
"HttpClientCredentialType": 5,
"EndpointAdress": "https://service/DocumentSecuredService",
"CertificateSubjectName": "CertName"
}
Here is ConnectedService.json generated in Net 6 from a local wsdl file
{
"ExtendedData": {
"inputs": [
"../wsdl/DocumentSecuredServiceV002.wsdl"
],
"collectionTypes": [
"System.Array",
"System.Collections.Generic.Dictionary`2"
],
"namespaceMappings": [
"*, BslDocumentService"
],
"references": [
"AutoMapper, {AutoMapper, 12.0.0}",
"C:\\Source\\TIPI\\Tipi Backend\\Tipi.Backend.Web\\Tipi.Backend.Web.Services\\bin\\Debug\\net6.0\\Tipi.Backend.Web.Client.Models.dll",
"C:\\Source\\TIPI\\Tipi Backend\\Tipi.Backend.Web\\Tipi.Backend.Web.Services\\bin\\Debug\\net6.0\\Tipi.Backend.Web.Core.dll",
"C:\\Source\\TIPI\\Tipi Backend\\Tipi.Backend.Web\\Tipi.Backend.Web.Services\\bin\\Debug\\net6.0\\Tipi.Backend.Web.Data.dll",
"Microsoft.AspNetCore.Cryptography.Internal, {Microsoft.AspNetCore.Cryptography.Internal, 6.0.11}",
"Microsoft.AspNetCore.Cryptography.KeyDerivation, {Microsoft.AspNetCore.Cryptography.KeyDerivation, 6.0.11}",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore, {Microsoft.AspNetCore.Identity.EntityFrameworkCore, 6.0.11}",
"Microsoft.Bcl.AsyncInterfaces, {Microsoft.Bcl.AsyncInterfaces, 5.0.0}",
"Microsoft.Data.Sqlite, {Microsoft.Data.Sqlite.Core, 6.0.11}",
"Microsoft.EntityFrameworkCore, {Microsoft.EntityFrameworkCore, 6.0.11}",
"Microsoft.EntityFrameworkCore.Abstractions, {Microsoft.EntityFrameworkCore.Abstractions, 6.0.11}",
"Microsoft.EntityFrameworkCore.Relational, {Microsoft.EntityFrameworkCore.Relational, 6.0.11}",
"Microsoft.EntityFrameworkCore.Sqlite, {Microsoft.EntityFrameworkCore.Sqlite.Core, 6.0.11}",
"Microsoft.Extensions.Caching.Abstractions, {Microsoft.Extensions.Caching.Abstractions, 6.0.0}",
"Microsoft.Extensions.Caching.Memory, {Microsoft.Extensions.Caching.Memory, 6.0.1}",
"Microsoft.Extensions.Configuration.Abstractions, {Microsoft.Extensions.Configuration.Abstractions, 6.0.0}",
"Microsoft.Extensions.DependencyInjection, {Microsoft.Extensions.DependencyInjection, 6.0.1}",
"Microsoft.Extensions.DependencyInjection.Abstractions, {Microsoft.Extensions.DependencyInjection.Abstractions, 6.0.0}",
"Microsoft.Extensions.DependencyModel, {Microsoft.Extensions.DependencyModel, 6.0.0}",
"Microsoft.Extensions.Identity.Core, {Microsoft.Extensions.Identity.Core, 6.0.11}",
"Microsoft.Extensions.Identity.Stores, {Microsoft.Extensions.Identity.Stores, 6.0.11}",
"Microsoft.Extensions.Localization.Abstractions, {Microsoft.Extensions.Localization.Abstractions, 7.0.0}",
"Microsoft.Extensions.Logging, {Microsoft.Extensions.Logging, 6.0.0}",
"Microsoft.Extensions.Logging.Abstractions, {Microsoft.Extensions.Logging.Abstractions, 6.0.0}",
"Microsoft.Extensions.ObjectPool, {Microsoft.Extensions.ObjectPool, 5.0.10}",
"Microsoft.Extensions.Options, {Microsoft.Extensions.Options, 6.0.0}",
"Microsoft.Extensions.Primitives, {Microsoft.Extensions.Primitives, 6.0.0}",
"Microsoft.IdentityModel.Logging, {Microsoft.IdentityModel.Logging, 6.8.0}",
"Microsoft.IdentityModel.Protocols.WsTrust, {Microsoft.IdentityModel.Protocols.WsTrust, 6.8.0}",
"Microsoft.IdentityModel.Tokens, {Microsoft.IdentityModel.Tokens, 6.8.0}",
"Microsoft.IdentityModel.Tokens.Saml, {Microsoft.IdentityModel.Tokens.Saml, 6.8.0}",
"Microsoft.IdentityModel.Xml, {Microsoft.IdentityModel.Xml, 6.8.0}",
"Microsoft.Win32.SystemEvents, {Microsoft.Win32.SystemEvents, 7.0.0}",
"Newtonsoft.Json, {Newtonsoft.Json, 13.0.1}",
"NLog, {NLog, 5.0.5}",
"NLog.Extensions.Logging, {NLog.Extensions.Logging, 5.1.0}",
"NLog.Web.AspNetCore, {NLog.Web.AspNetCore, 5.1.5}",
"Oracle.EntityFrameworkCore, {Oracle.EntityFrameworkCore, 6.21.90}",
"Oracle.ManagedDataAccess, {Oracle.ManagedDataAccess.Core, 3.21.90}",
"SQLitePCLRaw.batteries_v2, {SQLitePCLRaw.bundle_e_sqlite3, 2.0.6}",
"SQLitePCLRaw.core, {SQLitePCLRaw.core, 2.0.6}",
"SQLitePCLRaw.provider.e_sqlite3, {SQLitePCLRaw.provider.e_sqlite3, 2.0.6}",
"System.Collections.Immutable, {System.Collections.Immutable, 6.0.0}",
"System.Configuration.ConfigurationManager, {System.Configuration.ConfigurationManager, 6.0.0}",
"System.Diagnostics.DiagnosticSource, {System.Diagnostics.DiagnosticSource, 6.0.0}",
"System.Diagnostics.PerformanceCounter, {System.Diagnostics.PerformanceCounter, 6.0.0}",
"System.DirectoryServices, {System.DirectoryServices, 7.0.0}",
"System.DirectoryServices.Protocols, {System.DirectoryServices.Protocols, 5.0.1}",
"System.Drawing.Common, {System.Drawing.Common, 7.0.0}",
"System.Formats.Asn1, {System.Formats.Asn1, 6.0.0}",
"System.IO, {System.IO, 4.3.0}",
"System.Reflection.DispatchProxy, {System.Reflection.DispatchProxy, 4.7.1}",
"System.Runtime, {System.Runtime, 4.3.0}",
"System.Runtime.CompilerServices.Unsafe, {System.Runtime.CompilerServices.Unsafe, 6.0.0}",
"System.Security.AccessControl, {System.Security.AccessControl, 6.0.0}",
"System.Security.Cryptography.Cng, {System.Security.Cryptography.Cng, 4.5.0}",
"System.Security.Cryptography.Pkcs, {System.Security.Cryptography.Pkcs, 6.0.1}",
"System.Security.Cryptography.ProtectedData, {System.Security.Cryptography.ProtectedData, 6.0.0}",
"System.Security.Cryptography.Xml, {System.Security.Cryptography.Xml, 6.0.1}",
"System.Security.Permissions, {System.Security.Permissions, 7.0.0}",
"System.Security.Principal.Windows, {System.Security.Principal.Windows, 5.0.0}",
"System.ServiceModel, {System.ServiceModel.Primitives, 4.10.2}",
"System.ServiceModel.Duplex, {System.ServiceModel.Duplex, 4.10.2}",
"System.ServiceModel.Federation, {System.ServiceModel.Federation, 4.10.2}",
"System.ServiceModel.Http, {System.ServiceModel.Http, 4.10.2}",
"System.ServiceModel.NetTcp, {System.ServiceModel.NetTcp, 4.10.2}",
"System.ServiceModel.Primitives, {System.ServiceModel.Primitives, 4.10.2}",
"System.ServiceModel.Security, {System.ServiceModel.Security, 4.10.2}",
"System.Text.Encoding, {System.Text.Encoding, 4.3.0}",
"System.Text.Encodings.Web, {System.Text.Encodings.Web, 6.0.0}",
"System.Text.Json, {System.Text.Json, 6.0.0}",
"System.Threading.Tasks, {System.Threading.Tasks, 4.3.0}",
"System.Windows.Extensions, {System.Windows.Extensions, 7.0.0}",
"System.Xml.ReaderWriter, {System.Xml.ReaderWriter, 4.3.0}",
"System.Xml.XmlDocument, {System.Xml.XmlDocument, 4.3.0}"
],
"sync": true,
"targetFramework": "net6.0",
"typeReuseMode": "All"
}
}
I have updated to latest version System.ServiceModel packages (6.0.0). In previous backend it was 4.0.0 version. I have used svcUtil version 2.1.0
I have compared both service object instanciate and i can not see any differences that could explain the slow performances
I have also tried to change few settings and to use other bindings (NetTcpBinding and WSHttpBinding) without success
We work only on client side of WCF service
Thank you in advance for your help
EDIT
I have improved new times to 500ms thanks to this change https://github.com/dotnet/wcf/issues/5002
答案1
得分: 1
在 WCF 团队的 GitHub 上创建了一个问题后,提出了两个解决方案:
- 使用 HttpClientFactory,但需要对我们的代码进行大量修改。
- 在第一个客户端实例之前添加
DocumentSecuredServiceV002Client.CacheSetting = CacheSetting.AlwaysOn;
。我们这样做后,性能比我们当前的 API 更快。
英文:
After creating an issue on WCF team github , two solutions have been proposed :
-
Work HttpClientFactory but it would need a lot of modifications on our code
-
Add
DocumentSecuredServiceV002Client.CacheSetting = CacheSetting.AlwaysOn;
before first client instance. We did this and performances were faster than our current API
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论