Optimierung von Performance und Skalierbarkeit
- Assembly-Versionen auf 2.2.0.0 und 2.8.0.0 aktualisiert. - Verbesserte Steuerung von `ScheduleScrollUpdate` in SnapPanel. - Erhöhte Serverkapazität für 300+ Benutzer in EDMIService. - Einführung von Windows-Authentifizierungs-Caching. - Neuer atomarer Tabellen-Caching-Mechanismus in EDMIService. - Lock-Mechanismus in JobListener optimiert (Instanz-Lock). - Verbesserte Performance durch kürzere Lock-Dauern in SaveDataTables. - Logging und Snapshot-Veröffentlichung nach Job-Ausführung optimiert. - Allgemeine Skalierungs- und Performance-Verbesserungen.
This commit is contained in:
@@ -71,16 +71,37 @@ Public Class EDMIService
|
||||
oThrottling = New ServiceThrottlingBehavior()
|
||||
Config.Description.Behaviors.Add(oThrottling)
|
||||
End If
|
||||
|
||||
Dim oCpuCores As Integer = Environment.ProcessorCount
|
||||
oThrottling.MaxConcurrentCalls = Math.Max(200, 32 * oCpuCores) ' war 16×
|
||||
oThrottling.MaxConcurrentSessions = Math.Max(400, 150 * oCpuCores) ' war 100×
|
||||
|
||||
' ← Server-seitige Kapazität für 300+ User
|
||||
oThrottling.MaxConcurrentCalls = Math.Max(400, 32 * oCpuCores)
|
||||
oThrottling.MaxConcurrentSessions = Math.Max(600, 150 * oCpuCores)
|
||||
oThrottling.MaxConcurrentInstances = Integer.MaxValue
|
||||
|
||||
' ── Werte für späteres Logging speichern ─────────────────────────────
|
||||
' ← Server-seitiges Binding: toleriert langsamere Clients (alte DLL!)
|
||||
Dim oNetTcpBinding = TryCast(oBinding, NetTcpBinding)
|
||||
If oNetTcpBinding IsNot Nothing Then
|
||||
' ← Konsistent mit Channel.GetBinding(): CPUs × 50, mind. 500
|
||||
Dim oMaxConnections = Math.Max(500, oCpuCores * 50)
|
||||
oNetTcpBinding.SendTimeout = TimeSpan.FromMinutes(5)
|
||||
oNetTcpBinding.ReceiveTimeout = TimeSpan.FromMinutes(10)
|
||||
oNetTcpBinding.OpenTimeout = TimeSpan.FromMinutes(2)
|
||||
oNetTcpBinding.MaxConnections = oMaxConnections
|
||||
LogConfig?.GetLogger()?.Debug("✔ NetTcpBinding configured | MaxConnections=[{0}] | SendTimeout=[{1}]",
|
||||
oMaxConnections, oNetTcpBinding.SendTimeout)
|
||||
End If
|
||||
' ← NEU: Windows Auth Caching – reduziert DC-Anfragen bei parallelen Verbindungen
|
||||
Dim oServiceCredentials = Config.Description.Behaviors.Find(Of ServiceCredentials)()
|
||||
If oServiceCredentials Is Nothing Then
|
||||
oServiceCredentials = New ServiceCredentials()
|
||||
Config.Description.Behaviors.Add(oServiceCredentials)
|
||||
End If
|
||||
' Gruppenauflösung deaktivieren: weniger DC-Roundtrips pro Verbindung
|
||||
oServiceCredentials.WindowsAuthentication.IncludeWindowsGroups = False
|
||||
_ConfiguredEndpoint = oBaseAddress.ToString()
|
||||
_ConfiguredMaxCalls = oThrottling.MaxConcurrentCalls
|
||||
_ConfiguredMaxSessions = oThrottling.MaxConcurrentSessions
|
||||
' ─────────────────────────────────────────────────────────────────────
|
||||
End Sub
|
||||
|
||||
Public Sub New()
|
||||
@@ -201,27 +222,39 @@ Public Class EDMIService
|
||||
|
||||
|
||||
#Region "=== Database ==="
|
||||
Private Shared ReadOnly _CachedTablesLock As New Object()
|
||||
Private Shared _CachedTablesSnapshot As List(Of String) = Nothing
|
||||
Private Shared _CachedTablesExpiry As DateTime = DateTime.MinValue
|
||||
' ── CachedTables-Snapshot: wird vom JobListener nach dem DatatableJob gesetzt.
|
||||
' GetCachedTables() liest nur diesen Snapshot – ruft GlobalState nie direkt auf
|
||||
' und blockiert daher nicht, auch wenn der DatatableJob den TableStore sperrt.
|
||||
Private Class CachedTablesState
|
||||
Public ReadOnly Tables As String()
|
||||
Public ReadOnly Timestamp As DateTime
|
||||
Public Sub New(pTables As String(), pTimestamp As DateTime)
|
||||
Tables = pTables
|
||||
Timestamp = pTimestamp
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
Private Shared _CachedTablesState As CachedTablesState = New CachedTablesState(Array.Empty(Of String)(), DateTime.MinValue)
|
||||
|
||||
''' <summary>
|
||||
''' Wird vom JobListener nach erfolgreichem DatatableJob aufgerufen.
|
||||
''' Setzt den Snapshot atomar – kein Lock nötig, da Array-Referenzzuweisung auf CLR atomar ist.
|
||||
''' </summary>
|
||||
Public Shared Sub PublishCachedTablesSnapshot(pTableNames As IEnumerable(Of String))
|
||||
Dim oTables = pTableNames.Select(Function(t) t.ToUpper()).ToArray()
|
||||
Dim oNewState = New CachedTablesState(oTables, DateTime.Now)
|
||||
' Atomar: Timestamp und Array immer gemeinsam sichtbar
|
||||
Interlocked.Exchange(_CachedTablesState, oNewState)
|
||||
LogConfig?.GetLogger()?.Debug("PublishCachedTablesSnapshot: [{0}] tables published at [{1}]",
|
||||
oTables.Length, oNewState.Timestamp.ToString("HH:mm:ss"))
|
||||
End Sub
|
||||
|
||||
Public Function GetCachedTables() As List(Of String) Implements IEDMIService.GetCachedTables
|
||||
Try
|
||||
SyncLock _CachedTablesLock
|
||||
If _CachedTablesSnapshot Is Nothing OrElse DateTime.Now > _CachedTablesExpiry Then
|
||||
_Logger.Debug("GetCachedTables: Refreshing cache...")
|
||||
_CachedTablesSnapshot = GlobalState.GetCachedTables()
|
||||
_CachedTablesExpiry = DateTime.Now.AddSeconds(30)
|
||||
_Logger.Debug("GetCachedTables: Cache refreshed | Count=[{0}] | NextExpiry=[{1}]",
|
||||
_CachedTablesSnapshot.Count, _CachedTablesExpiry.ToString("HH:mm:ss"))
|
||||
End If
|
||||
Return _CachedTablesSnapshot.ToList()
|
||||
End SyncLock
|
||||
Catch ex As Exception
|
||||
_Logger.Error("GetCachedTables failed | User=[{0}] | Error=[{1}]", _Username, ex.Message)
|
||||
_Logger.Error(ex)
|
||||
Return New List(Of String)
|
||||
End Try
|
||||
Dim oState = _CachedTablesState ' atomarer Read – eine Referenz
|
||||
_Logger.Debug("GetCachedTables: returning snapshot | Count=[{0}] | SnapshotAge=[{1}s]",
|
||||
oState.Tables.Length,
|
||||
CInt((DateTime.Now - oState.Timestamp).TotalSeconds))
|
||||
Return oState.Tables.ToList()
|
||||
End Function
|
||||
|
||||
Public Function ReturnDatatableFromCache(Name As String, FilterExpression As String, SortByColumn As String) As TableResult Implements IEDMIService.ReturnDatatableFromCache
|
||||
|
||||
Reference in New Issue
Block a user