Dreamine.Communication.Core
1.0.2
Dreamine.Communication.Core 통신 기능과 관련 API를 제공합니다.
Toggle main menu visibility
로딩중...
검색중...
일치하는것 없음
ResilientMessageTransport.cs
이 파일의 문서화 페이지로 가기
1
using
Dreamine.Communication.Abstractions.Enums;
2
using
Dreamine.Communication.Abstractions.Interfaces;
3
using
Dreamine.Communication.Abstractions.Models;
4
using
Dreamine.Communication.Abstractions.Options;
5
using
Dreamine.Communication.Core.Queues
;
6
7
namespace
Dreamine.Communication.Core.Resilience
;
8
17
public
sealed
class
ResilientMessageTransport
: IMessageTransport
18
{
27
private
readonly IMessageTransport
_innerTransport
;
36
private
readonly ReconnectPolicy
_reconnectPolicy
;
45
private
readonly OutboundQueueOptions
_queueOptions
;
54
private
readonly
IOutboundMessageQueue
_outboundQueue
;
63
private
readonly
ReconnectDelayCalculator
_delayCalculator
;
72
private
readonly SemaphoreSlim
_flushLock
=
new
(1, 1);
81
private
readonly
object
_loopSync
=
new
();
90
private
readonly
object
_notifySync
=
new
();
91
100
private
CancellationTokenSource
_lifetimeCts
=
new
();
109
private
Task?
_resilienceLoopTask
;
118
private
int
_manualDisconnectRequested
= 1;
127
private
int
_resilienceLoopRequested
;
136
private
int
_disposeGuard
;
// Interlocked: 0 = live, 1 = disposed
145
private
bool
_disposed
;
154
private
ConnectionState
_lastNotifiedState
= ConnectionState.Disconnected;
155
204
public
ResilientMessageTransport
(
205
IMessageTransport innerTransport,
206
ReconnectPolicy? reconnectPolicy =
null
,
207
OutboundQueueOptions? queueOptions =
null
,
208
IOutboundMessageQueue
? outboundQueue =
null
)
209
{
210
_innerTransport
= innerTransport ??
throw
new
ArgumentNullException(nameof(innerTransport));
211
_reconnectPolicy
= reconnectPolicy ??
new
ReconnectPolicy();
212
_queueOptions
= queueOptions ??
new
OutboundQueueOptions();
213
_outboundQueue
= outboundQueue ??
new
InMemoryOutboundMessageQueue
(
_queueOptions
);
214
_delayCalculator
=
new
ReconnectDelayCalculator
(
_reconnectPolicy
);
215
216
_lastNotifiedState
=
State
;
217
_innerTransport.MessageReceived +=
OnInnerMessageReceived
;
218
}
219
228
public
event
EventHandler<MessageEnvelope>?
MessageReceived
;
229
238
public
event
EventHandler<ConnectionState>?
StateChanged
;
239
248
public
event
EventHandler<int>?
QueueCountChanged
;
249
258
public
TransportKind
Kind
=>
_innerTransport
.Kind;
259
268
public
ConnectionState
State
269
{
270
get
271
{
272
if
(
IsOperationalState
(
_innerTransport
.State))
273
{
274
return
_innerTransport
.State;
275
}
276
277
if
(
_reconnectPolicy
.Enabled &&
278
!
IsManualDisconnectRequested
&&
279
IsResilienceLoopRequested
)
280
{
281
return
ConnectionState.Connecting;
282
}
283
284
return
_innerTransport
.State;
285
}
286
}
287
296
public
int
QueuedMessageCount
=>
_outboundQueue
.Count;
297
338
public
async Task
ConnectAsync
(CancellationToken cancellationToken =
default
)
339
{
340
ThrowIfDisposed
();
341
342
SetManualDisconnectRequested
(
false
);
343
EnsureResilienceLoopStarted
();
344
NotifyStateChangedIfNeeded
();
345
346
try
347
{
348
await
_innerTransport
.ConnectAsync(cancellationToken).ConfigureAwait(
false
);
349
NotifyStateChangedIfNeeded
();
350
351
if
(
_queueOptions
.FlushOnReconnect)
352
{
353
await
FlushQueueAsync
(cancellationToken).ConfigureAwait(
false
);
354
}
355
}
356
catch
357
{
358
NotifyStateChangedIfNeeded
();
359
360
if
(!
_reconnectPolicy
.Enabled)
361
{
362
throw
;
363
}
364
365
// 자동 재접속 모드에서는 최초 연결 실패를 치명 오류로 보지 않습니다.
366
// 이후 백그라운드 재접속 루프가 계속 연결을 시도합니다.
367
}
368
}
369
410
public
async Task
DisconnectAsync
(CancellationToken cancellationToken =
default
)
411
{
412
ThrowIfDisposed
();
413
414
SetManualDisconnectRequested
(
true
);
415
SetResilienceLoopRequested
(
false
);
416
NotifyStateChangedIfNeeded
();
417
418
await
_innerTransport
.DisconnectAsync(cancellationToken).ConfigureAwait(
false
);
419
NotifyStateChangedIfNeeded
();
420
}
421
486
public
async Task
SendAsync
(
487
MessageEnvelope message,
488
CancellationToken cancellationToken =
default
)
489
{
490
ArgumentNullException.ThrowIfNull(message);
491
ThrowIfDisposed
();
492
493
SetManualDisconnectRequested
(
false
);
494
495
if
(
IsOperationalState
(
_innerTransport
.State))
496
{
497
try
498
{
499
await
_innerTransport
.SendAsync(message, cancellationToken).ConfigureAwait(
false
);
500
NotifyStateChangedIfNeeded
();
501
return
;
502
}
503
catch
504
{
505
await
HandleDisconnectedSendAsync
(message, cancellationToken).ConfigureAwait(
false
);
506
EnsureResilienceLoopStarted
();
507
NotifyStateChangedIfNeeded
();
508
return
;
509
}
510
}
511
512
await
HandleDisconnectedSendAsync
(message, cancellationToken).ConfigureAwait(
false
);
513
EnsureResilienceLoopStarted
();
514
NotifyStateChangedIfNeeded
();
515
}
516
557
public
async Task
FlushQueueAsync
(CancellationToken cancellationToken =
default
)
558
{
559
ThrowIfDisposed
();
560
561
if
(!
_queueOptions
.FlushOnReconnect)
562
{
563
return
;
564
}
565
566
await
_flushLock
.WaitAsync(cancellationToken).ConfigureAwait(
false
);
567
568
try
569
{
570
while
(
IsOperationalState
(
_innerTransport
.State))
571
{
572
cancellationToken.ThrowIfCancellationRequested();
573
574
var queuedMessage = await
_outboundQueue
575
.TryDequeueAsync(cancellationToken)
576
.ConfigureAwait(
false
);
577
578
if
(queuedMessage is
null
)
579
{
580
NotifyQueueCountChanged
();
581
NotifyStateChangedIfNeeded
();
582
return
;
583
}
584
585
NotifyQueueCountChanged
();
586
587
if
(queuedMessage.IsExpired(
588
_queueOptions
.MaxMessageAge,
589
DateTimeOffset.UtcNow))
590
{
591
continue
;
592
}
593
594
try
595
{
596
await
_innerTransport
597
.SendAsync(queuedMessage.Message, cancellationToken)
598
.ConfigureAwait(
false
);
599
600
NotifyQueueCountChanged
();
601
NotifyStateChangedIfNeeded
();
602
}
603
catch
(Exception ex)
604
{
605
await
_outboundQueue
606
.EnqueueFrontAsync(queuedMessage.WithFailure(ex), cancellationToken)
607
.ConfigureAwait(
false
);
608
609
NotifyQueueCountChanged
();
610
EnsureResilienceLoopStarted
();
611
NotifyStateChangedIfNeeded
();
612
return
;
613
}
614
}
615
}
616
finally
617
{
618
_flushLock
.Release();
619
}
620
}
621
638
public
async ValueTask
DisposeAsync
()
639
{
640
// Interlocked guard prevents concurrent double-dispose.
641
if
(Interlocked.CompareExchange(ref
_disposeGuard
, 1, 0) != 0)
642
{
643
return
;
644
}
645
646
_disposed
=
true
;
647
SetManualDisconnectRequested
(
true
);
648
SetResilienceLoopRequested
(
false
);
649
650
_innerTransport.MessageReceived -=
OnInnerMessageReceived
;
651
652
try
653
{
654
await
_lifetimeCts
.CancelAsync().ConfigureAwait(
false
);
655
}
656
catch
657
{
658
// Dispose 경로에서는 취소 실패를 무시합니다.
659
}
660
661
if
(
_resilienceLoopTask
is not
null
)
662
{
663
try
664
{
665
await
_resilienceLoopTask
.ConfigureAwait(
false
);
666
}
667
catch
668
{
669
// Dispose 경로에서는 백그라운드 루프 예외를 무시합니다.
670
}
671
}
672
673
_lifetimeCts
.Dispose();
674
_flushLock
.Dispose();
675
676
await
_innerTransport
.DisposeAsync().ConfigureAwait(
false
);
677
}
678
719
private
async Task
HandleDisconnectedSendAsync
(
720
MessageEnvelope message,
721
CancellationToken cancellationToken)
722
{
723
switch
(
_queueOptions
.DisconnectedSendPolicy)
724
{
725
case
DisconnectedSendPolicy.Fail:
726
throw
new
InvalidOperationException(
"Transport is not connected."
);
727
728
case
DisconnectedSendPolicy.Queue:
729
await
_outboundQueue
730
.EnqueueAsync(
new
QueuedOutboundMessage
(message), cancellationToken)
731
.ConfigureAwait(
false
);
732
733
NotifyQueueCountChanged
();
734
return
;
735
736
case
DisconnectedSendPolicy.WaitUntilConnected:
737
await
WaitUntilConnectedAsync
(cancellationToken).ConfigureAwait(
false
);
738
await
_innerTransport
.SendAsync(message, cancellationToken).ConfigureAwait(
false
);
739
NotifyStateChangedIfNeeded
();
740
return
;
741
742
default
:
743
throw
new
InvalidOperationException(
744
$
"Unsupported disconnected send policy: {_queueOptions.DisconnectedSendPolicy}"
);
745
}
746
}
747
780
private
async Task
WaitUntilConnectedAsync
(CancellationToken cancellationToken)
781
{
782
EnsureResilienceLoopStarted
();
783
NotifyStateChangedIfNeeded
();
784
785
while
(!
IsOperationalState
(
_innerTransport
.State))
786
{
787
cancellationToken.ThrowIfCancellationRequested();
788
await Task.Delay(
_reconnectPolicy
.WatchInterval, cancellationToken)
789
.ConfigureAwait(
false
);
790
}
791
}
792
801
private
void
EnsureResilienceLoopStarted
()
802
{
803
if
(!
_reconnectPolicy
.Enabled)
804
{
805
return
;
806
}
807
808
lock (
_loopSync
)
809
{
810
if
(
_disposed
)
811
{
812
return
;
813
}
814
815
SetResilienceLoopRequested
(
true
);
816
817
if
(
_resilienceLoopTask
is { IsCompleted:
false
})
818
{
819
return
;
820
}
821
822
if
(
_lifetimeCts
.IsCancellationRequested)
823
{
824
_lifetimeCts
.Dispose();
825
_lifetimeCts
=
new
CancellationTokenSource();
826
}
827
828
_resilienceLoopTask
= Task.Run(
829
() =>
RunResilienceLoopAsync
(
_lifetimeCts
.Token),
830
CancellationToken.None);
831
}
832
}
833
858
private
async Task
RunResilienceLoopAsync
(CancellationToken cancellationToken)
859
{
860
var reconnectAttempt = 0;
861
862
while
(!cancellationToken.IsCancellationRequested)
863
{
864
NotifyStateChangedIfNeeded
();
865
866
if
(
IsManualDisconnectRequested
)
867
{
868
await
DelaySafelyAsync
(
869
_reconnectPolicy
.WatchInterval,
870
cancellationToken)
871
.ConfigureAwait(
false
);
872
873
continue
;
874
}
875
876
if
(
IsOperationalState
(
_innerTransport
.State))
877
{
878
reconnectAttempt = 0;
879
NotifyStateChangedIfNeeded
();
880
881
if
(
_queueOptions
.FlushOnReconnect &&
_outboundQueue
.Count > 0)
882
{
883
await
FlushQueueAsync
(cancellationToken).ConfigureAwait(
false
);
884
}
885
886
await
DelaySafelyAsync
(
887
_reconnectPolicy
.WatchInterval,
888
cancellationToken)
889
.ConfigureAwait(
false
);
890
891
continue
;
892
}
893
894
if
(
_innerTransport
.State == ConnectionState.Disconnecting)
895
{
896
await
DelaySafelyAsync
(
897
_reconnectPolicy
.WatchInterval,
898
cancellationToken)
899
.ConfigureAwait(
false
);
900
901
continue
;
902
}
903
904
if
(
_reconnectPolicy
.MaxRetryCount is not
null
&&
905
reconnectAttempt >=
_reconnectPolicy
.MaxRetryCount.Value)
906
{
907
await
DelaySafelyAsync
(
908
_reconnectPolicy
.MaxDelay,
909
cancellationToken)
910
.ConfigureAwait(
false
);
911
912
continue
;
913
}
914
915
reconnectAttempt++;
916
NotifyStateChangedIfNeeded
();
917
918
try
919
{
920
await
_innerTransport
.ConnectAsync(cancellationToken).ConfigureAwait(
false
);
921
reconnectAttempt = 0;
922
NotifyStateChangedIfNeeded
();
923
924
if
(
_queueOptions
.FlushOnReconnect)
925
{
926
await
FlushQueueAsync
(cancellationToken).ConfigureAwait(
false
);
927
}
928
}
929
catch
930
{
931
NotifyStateChangedIfNeeded
();
932
933
var delay =
_delayCalculator
.Calculate(reconnectAttempt);
934
935
await
DelaySafelyAsync
(delay, cancellationToken)
936
.ConfigureAwait(
false
);
937
}
938
}
939
}
940
965
private
static
bool
IsOperationalState
(ConnectionState state)
966
{
967
return
state is ConnectionState.Connected or ConnectionState.Listening;
968
}
969
978
private
bool
IsManualDisconnectRequested
=>
979
Volatile.Read(ref
_manualDisconnectRequested
) != 0;
980
989
private
bool
IsResilienceLoopRequested
=>
990
Volatile.Read(ref
_resilienceLoopRequested
) != 0;
991
1008
private
void
SetManualDisconnectRequested
(
bool
value)
1009
{
1010
Interlocked.Exchange(ref
_manualDisconnectRequested
, value ? 1 : 0);
1011
}
1012
1029
private
void
SetResilienceLoopRequested
(
bool
value)
1030
{
1031
Interlocked.Exchange(ref
_resilienceLoopRequested
, value ? 1 : 0);
1032
}
1033
1066
private
static
async Task
DelaySafelyAsync
(
1067
TimeSpan delay,
1068
CancellationToken cancellationToken)
1069
{
1070
if
(delay <= TimeSpan.Zero)
1071
{
1072
delay = TimeSpan.FromMilliseconds(1);
1073
}
1074
1075
try
1076
{
1077
await Task.Delay(delay, cancellationToken).ConfigureAwait(
false
);
1078
}
1079
catch
(OperationCanceledException)
1080
{
1081
// 정상적인 종료 경로입니다.
1082
}
1083
}
1084
1109
private
void
OnInnerMessageReceived
(
object
? sender, MessageEnvelope message)
1110
{
1111
MessageReceived
?.Invoke(
this
, message);
1112
}
1113
1122
private
void
NotifyStateChangedIfNeeded
()
1123
{
1124
var currentState =
State
;
1125
EventHandler<ConnectionState>? handler;
1126
1127
lock (
_notifySync
)
1128
{
1129
if
(
_lastNotifiedState
== currentState)
1130
{
1131
return
;
1132
}
1133
1134
_lastNotifiedState
= currentState;
1135
handler =
StateChanged
;
1136
}
1137
1138
handler?.Invoke(
this
, currentState);
1139
}
1140
1149
private
void
NotifyQueueCountChanged
()
1150
{
1151
QueueCountChanged
?.Invoke(
this
,
_outboundQueue
.Count);
1152
}
1153
1170
private
void
ThrowIfDisposed
()
1171
{
1172
ObjectDisposedException.ThrowIf(
_disposed
,
this
);
1173
}
1174
}
Dreamine.Communication.Core.Queues
Definition
InMemoryOutboundMessageQueue.cs:3
Dreamine.Communication.Core.Resilience
Definition
ReconnectDelayCalculator.cs:3
Dreamine.Communication.Core.Queues.InMemoryOutboundMessageQueue
Definition
InMemoryOutboundMessageQueue.cs:14
Dreamine.Communication.Core.Queues.IOutboundMessageQueue
Definition
IOutboundMessageQueue.cs:12
Dreamine.Communication.Core.Queues.QueuedOutboundMessage
Definition
QueuedOutboundMessage.cs:14
Dreamine.Communication.Core.Resilience.ReconnectDelayCalculator
Definition
ReconnectDelayCalculator.cs:14
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._disposed
bool _disposed
Definition
ResilientMessageTransport.cs:145
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.NotifyQueueCountChanged
void NotifyQueueCountChanged()
Definition
ResilientMessageTransport.cs:1149
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._disposeGuard
int _disposeGuard
Definition
ResilientMessageTransport.cs:136
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._delayCalculator
readonly ReconnectDelayCalculator _delayCalculator
Definition
ResilientMessageTransport.cs:63
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._reconnectPolicy
readonly ReconnectPolicy _reconnectPolicy
Definition
ResilientMessageTransport.cs:36
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.EnsureResilienceLoopStarted
void EnsureResilienceLoopStarted()
Definition
ResilientMessageTransport.cs:801
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.ConnectAsync
async Task ConnectAsync(CancellationToken cancellationToken=default)
Definition
ResilientMessageTransport.cs:338
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.StateChanged
EventHandler< ConnectionState >? StateChanged
Definition
ResilientMessageTransport.cs:238
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._queueOptions
readonly OutboundQueueOptions _queueOptions
Definition
ResilientMessageTransport.cs:45
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.SendAsync
async Task SendAsync(MessageEnvelope message, CancellationToken cancellationToken=default)
Definition
ResilientMessageTransport.cs:486
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.SetResilienceLoopRequested
void SetResilienceLoopRequested(bool value)
Definition
ResilientMessageTransport.cs:1029
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._lifetimeCts
CancellationTokenSource _lifetimeCts
Definition
ResilientMessageTransport.cs:100
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.OnInnerMessageReceived
void OnInnerMessageReceived(object? sender, MessageEnvelope message)
Definition
ResilientMessageTransport.cs:1109
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._notifySync
readonly object _notifySync
Definition
ResilientMessageTransport.cs:90
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.DelaySafelyAsync
static async Task DelaySafelyAsync(TimeSpan delay, CancellationToken cancellationToken)
Definition
ResilientMessageTransport.cs:1066
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.ResilientMessageTransport
ResilientMessageTransport(IMessageTransport innerTransport, ReconnectPolicy? reconnectPolicy=null, OutboundQueueOptions? queueOptions=null, IOutboundMessageQueue? outboundQueue=null)
Definition
ResilientMessageTransport.cs:204
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.WaitUntilConnectedAsync
async Task WaitUntilConnectedAsync(CancellationToken cancellationToken)
Definition
ResilientMessageTransport.cs:780
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.IsManualDisconnectRequested
bool IsManualDisconnectRequested
Definition
ResilientMessageTransport.cs:978
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.IsOperationalState
static bool IsOperationalState(ConnectionState state)
Definition
ResilientMessageTransport.cs:965
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.IsResilienceLoopRequested
bool IsResilienceLoopRequested
Definition
ResilientMessageTransport.cs:989
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.FlushQueueAsync
async Task FlushQueueAsync(CancellationToken cancellationToken=default)
Definition
ResilientMessageTransport.cs:557
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.QueuedMessageCount
int QueuedMessageCount
Definition
ResilientMessageTransport.cs:296
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._lastNotifiedState
ConnectionState _lastNotifiedState
Definition
ResilientMessageTransport.cs:154
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._flushLock
readonly SemaphoreSlim _flushLock
Definition
ResilientMessageTransport.cs:72
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._manualDisconnectRequested
int _manualDisconnectRequested
Definition
ResilientMessageTransport.cs:118
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._innerTransport
readonly IMessageTransport _innerTransport
Definition
ResilientMessageTransport.cs:27
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.RunResilienceLoopAsync
async Task RunResilienceLoopAsync(CancellationToken cancellationToken)
Definition
ResilientMessageTransport.cs:858
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.QueueCountChanged
EventHandler< int >? QueueCountChanged
Definition
ResilientMessageTransport.cs:248
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.DisconnectAsync
async Task DisconnectAsync(CancellationToken cancellationToken=default)
Definition
ResilientMessageTransport.cs:410
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.HandleDisconnectedSendAsync
async Task HandleDisconnectedSendAsync(MessageEnvelope message, CancellationToken cancellationToken)
Definition
ResilientMessageTransport.cs:719
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._resilienceLoopTask
Task? _resilienceLoopTask
Definition
ResilientMessageTransport.cs:109
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.DisposeAsync
async ValueTask DisposeAsync()
Definition
ResilientMessageTransport.cs:638
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.ThrowIfDisposed
void ThrowIfDisposed()
Definition
ResilientMessageTransport.cs:1170
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.State
ConnectionState State
Definition
ResilientMessageTransport.cs:269
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.SetManualDisconnectRequested
void SetManualDisconnectRequested(bool value)
Definition
ResilientMessageTransport.cs:1008
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._outboundQueue
readonly IOutboundMessageQueue _outboundQueue
Definition
ResilientMessageTransport.cs:54
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.MessageReceived
EventHandler< MessageEnvelope >? MessageReceived
Definition
ResilientMessageTransport.cs:228
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._resilienceLoopRequested
int _resilienceLoopRequested
Definition
ResilientMessageTransport.cs:127
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.Kind
TransportKind Kind
Definition
ResilientMessageTransport.cs:258
Dreamine.Communication.Core.Resilience.ResilientMessageTransport.NotifyStateChangedIfNeeded
void NotifyStateChangedIfNeeded()
Definition
ResilientMessageTransport.cs:1122
Dreamine.Communication.Core.Resilience.ResilientMessageTransport._loopSync
readonly object _loopSync
Definition
ResilientMessageTransport.cs:81
Resilience
ResilientMessageTransport.cs
다음에 의해 생성됨 :
1.17.0