Verbesserungen: Thread-Sicherheit, Logging, WCF-Updates
Die Änderungen umfassen: - Einführung von `ReaderWriterLockSlim` für Thread-Sicherheit. - Optimierung von `IsOnline` mit atomaren Operationen. - Verbesserte Reconnect-Logik und Fehlerbehandlung. - Neue Methoden für Datenbankoperationen (`GetDatatableByName` etc.). - Gemeinsame `ChannelFactory` zur Ressourcenschonung. - Logging-Verbesserungen mit präziseren Meldungen und Symbolen. - Aktualisierte WCF-Bindings für höhere Verbindungszahlen und kürzere Timeouts. - Aktualisierte Service-Referenzen mit asynchronen Methoden. - Bereinigung von veralteten Methoden und Eigenschaften. - Assembly-Version auf `2.8.0.0` aktualisiert.
This commit is contained in:
@@ -8,10 +8,14 @@ Imports DigitalData.Modules.Logging
|
||||
Public Class Channel
|
||||
Inherits BaseClass
|
||||
|
||||
Private Shared ReadOnly _FactoryLock As New Object()
|
||||
Private Shared _SharedFactory As ChannelFactory(Of IEDMIServiceChannel) = Nothing
|
||||
Private Shared _SharedFactoryEndpoint As String = String.Empty
|
||||
|
||||
Private ReadOnly ChannelFactory As ChannelFactory(Of IEDMIServiceChannel)
|
||||
|
||||
Public Event Reconnect As EventHandler
|
||||
' Verbindungszähler
|
||||
|
||||
Private Shared _ActiveConnections As Integer = 0
|
||||
Public Shared ReadOnly Property ActiveConnections As Integer
|
||||
Get
|
||||
@@ -20,40 +24,91 @@ Public Class Channel
|
||||
End Property
|
||||
|
||||
Public Shared Function GetBinding(Optional AuthenticationMode As TcpClientCredentialType = TcpClientCredentialType.Windows) As NetTcpBinding
|
||||
Dim oMaxConnections = Math.Max(500, Environment.ProcessorCount * 50)
|
||||
|
||||
Return New NetTcpBinding() With {
|
||||
.MaxReceivedMessageSize = Constants.ChannelSettings.MAX_RECEIVED_MESSAGE_SIZE,
|
||||
.MaxBufferSize = Constants.ChannelSettings.MAX_BUFFER_SIZE,
|
||||
.MaxBufferPoolSize = Constants.ChannelSettings.MAX_BUFFER_POOL_SIZE,
|
||||
.ListenBacklog = Constants.ChannelSettings.MAX_CONNECTIONS,
|
||||
.MaxConnections = Constants.ChannelSettings.MAX_CONNECTIONS,
|
||||
.TransferMode = TransferMode.Streamed,
|
||||
.SendTimeout = TimeSpan.FromSeconds(30), ' ← War: 5min → Schnellere Fehlererkennung
|
||||
.ReceiveTimeout = TimeSpan.FromMinutes(10), ' Keepalive < Firewall-Timeout (3600s) ✓
|
||||
.OpenTimeout = TimeSpan.FromSeconds(30), ' ← Neu: Verbindungsaufbau begrenzen
|
||||
.CloseTimeout = TimeSpan.FromSeconds(30),
|
||||
.Security = New NetTcpSecurity() With {
|
||||
.Mode = SecurityMode.Transport,
|
||||
.Transport = New TcpTransportSecurity() With {
|
||||
.ClientCredentialType = AuthenticationMode
|
||||
.MaxReceivedMessageSize = Constants.ChannelSettings.MAX_RECEIVED_MESSAGE_SIZE,
|
||||
.MaxBufferSize = Constants.ChannelSettings.MAX_BUFFER_SIZE,
|
||||
.MaxBufferPoolSize = Constants.ChannelSettings.MAX_BUFFER_POOL_SIZE,
|
||||
.ListenBacklog = oMaxConnections,
|
||||
.MaxConnections = oMaxConnections,
|
||||
.TransferMode = TransferMode.Streamed,
|
||||
.SendTimeout = TimeSpan.FromSeconds(60),
|
||||
.ReceiveTimeout = TimeSpan.FromMinutes(10),
|
||||
.OpenTimeout = TimeSpan.FromSeconds(60),
|
||||
.CloseTimeout = TimeSpan.FromSeconds(30),
|
||||
.Security = New NetTcpSecurity() With {
|
||||
.Mode = SecurityMode.Transport,
|
||||
.Transport = New TcpTransportSecurity() With {
|
||||
.ClientCredentialType = AuthenticationMode
|
||||
}
|
||||
},
|
||||
.ReaderQuotas = New XmlDictionaryReaderQuotas() With {
|
||||
.MaxArrayLength = Constants.ChannelSettings.MAX_ARRAY_LENGTH,
|
||||
.MaxStringContentLength = Constants.ChannelSettings.MAX_STRING_CONTENT_LENGTH
|
||||
}
|
||||
},
|
||||
.ReaderQuotas = New XmlDictionaryReaderQuotas() With {
|
||||
.MaxArrayLength = Constants.ChannelSettings.MAX_ARRAY_LENGTH,
|
||||
.MaxStringContentLength = Constants.ChannelSettings.MAX_STRING_CONTENT_LENGTH
|
||||
}
|
||||
}
|
||||
End Function
|
||||
|
||||
Public Sub New(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct)
|
||||
MyBase.New(pLogConfig)
|
||||
ChannelFactory = GetChannelFactory(pServerAddress)
|
||||
ChannelFactory = GetOrCreateSharedFactory(pLogConfig, pServerAddress)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Gibt eine gemeinsame ChannelFactory zurück.
|
||||
''' Ein Connection-Pool für alle Client-Instanzen desselben Endpoints.
|
||||
''' </summary>
|
||||
Private Shared Function GetOrCreateSharedFactory(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct) As ChannelFactory(Of IEDMIServiceChannel)
|
||||
Dim oEndpoint = $"net.tcp://{pServerAddress.Host}:{pServerAddress.Port}/DigitalData/Services/Main"
|
||||
|
||||
' ← Volatile.Read: garantiert aktuellen Wert auch ohne Lock
|
||||
Dim oCurrentFactory = Volatile.Read(_SharedFactory)
|
||||
If oCurrentFactory IsNot Nothing AndAlso
|
||||
_SharedFactoryEndpoint = oEndpoint AndAlso
|
||||
(oCurrentFactory.State = CommunicationState.Opened OrElse
|
||||
oCurrentFactory.State = CommunicationState.Created) Then
|
||||
Return oCurrentFactory
|
||||
End If
|
||||
|
||||
SyncLock _FactoryLock
|
||||
' Double-check nach Lock
|
||||
If _SharedFactory IsNot Nothing AndAlso
|
||||
_SharedFactoryEndpoint = oEndpoint AndAlso
|
||||
(_SharedFactory.State = CommunicationState.Opened OrElse
|
||||
_SharedFactory.State = CommunicationState.Created) Then
|
||||
Return _SharedFactory
|
||||
End If
|
||||
|
||||
If _SharedFactory IsNot Nothing Then
|
||||
If _SharedFactory.State = CommunicationState.Faulted OrElse
|
||||
_SharedFactory.State = CommunicationState.Closing Then
|
||||
Try
|
||||
_SharedFactory.Abort()
|
||||
Catch
|
||||
End Try
|
||||
End If
|
||||
_SharedFactory = Nothing
|
||||
End If
|
||||
|
||||
Dim oLogger = pLogConfig.GetLogger()
|
||||
Dim oBinding = GetBinding()
|
||||
Dim oAddress = New EndpointAddress(oEndpoint)
|
||||
|
||||
oLogger.Debug("Creating shared ChannelFactory | Endpoint=[{0}] | MaxConnections=[{1}] | SendTimeout=[{2}] | OpenTimeout=[{3}]",
|
||||
oEndpoint, oBinding.MaxConnections, oBinding.SendTimeout, oBinding.OpenTimeout)
|
||||
|
||||
_SharedFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)
|
||||
_SharedFactoryEndpoint = oEndpoint
|
||||
Return _SharedFactory
|
||||
End SyncLock
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' Closes a channel gracefully, falls back to Abort on error
|
||||
''' </summary>
|
||||
Public Shared Sub CloseChannel(pChannel As IEDMIServiceChannel)
|
||||
If pChannel Is Nothing Then Return
|
||||
|
||||
Try
|
||||
If pChannel.State = CommunicationState.Opened Then
|
||||
pChannel.Close()
|
||||
@@ -61,31 +116,27 @@ Public Class Channel
|
||||
pChannel.State = CommunicationState.Closing Then
|
||||
pChannel.Abort()
|
||||
End If
|
||||
' CommunicationState.Created / Closed → nichts tun, Zähler nicht betroffen
|
||||
Catch ex As Exception
|
||||
Try
|
||||
pChannel.Abort()
|
||||
Catch
|
||||
' Ignore
|
||||
End Try
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Creates a channel and adds a Faulted-Handler
|
||||
''' </summary>
|
||||
''' <returns>A channel object</returns>
|
||||
Public Function GetChannel() As IEDMIServiceChannel
|
||||
Try
|
||||
Logger.Debug("Creating channel.")
|
||||
Logger.Debug("Creating channel from shared factory.")
|
||||
Dim oChannel = ChannelFactory.CreateChannel()
|
||||
|
||||
' Verbindung zählen beim Öffnen
|
||||
AddHandler oChannel.Opened, Sub()
|
||||
Dim oCount = Interlocked.Increment(_ActiveConnections)
|
||||
Logger.Debug("Channel opened. Active connections: [{0}]", oCount)
|
||||
End Sub
|
||||
|
||||
' Verbindung abziehen beim Schließen oder Fehler
|
||||
AddHandler oChannel.Closed, Sub()
|
||||
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
||||
Logger.Debug("Channel closed. Active connections: [{0}]", oCount)
|
||||
@@ -93,7 +144,7 @@ Public Class Channel
|
||||
|
||||
AddHandler oChannel.Faulted, Sub()
|
||||
Dim oCount = Interlocked.Decrement(_ActiveConnections)
|
||||
Logger.Warn("Channel faulted. Active connections: [{0}]", oCount)
|
||||
Logger.Warn("⚠ Channel faulted. Active connections: [{0}]", oCount)
|
||||
RaiseEvent Reconnect(Me, Nothing)
|
||||
End Sub
|
||||
|
||||
@@ -104,20 +155,4 @@ Public Class Channel
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Private Function GetChannelFactory(pServerAddress As ServerAddressStruct) As ChannelFactory(Of IEDMIServiceChannel)
|
||||
Dim oBinding = GetBinding()
|
||||
Dim oAddress = New EndpointAddress($"net.tcp://{pServerAddress.Host}:{pServerAddress.Port}/DigitalData/Services/Main")
|
||||
Logger.Debug("Creating ChannelFactory with Binding: SendTimeout=[{0}], ReceiveTimeout=[{1}], MaxConnections=[{2}], MaxBufferSize=[{3}], TransferMode=[{4}]",
|
||||
oBinding.SendTimeout,
|
||||
oBinding.ReceiveTimeout,
|
||||
oBinding.MaxConnections,
|
||||
oBinding.MaxBufferSize,
|
||||
oBinding.TransferMode)
|
||||
Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)
|
||||
Return oFactory
|
||||
End Function
|
||||
|
||||
End Class
|
||||
|
||||
|
||||
End Class
|
||||
Reference in New Issue
Block a user