44using System ;
55using System . Collections . Concurrent ;
66using System . Collections . Generic ;
7+ using System . Threading ;
8+ using System . Threading . Tasks ;
79
810namespace Convey . MessageBrokers . RabbitMQ . Clients ;
911
1012internal sealed class RabbitMqClient : IRabbitMqClient
1113{
1214 private const string EmptyContext = "{}" ;
1315
14- private readonly object _lockObject = new ( ) ;
16+ private readonly SemaphoreSlim _semaphore = new ( 1 , 1 ) ;
1517
1618 private readonly AppOptions _appOptions ;
1719 private readonly IConnection _connection ;
@@ -24,7 +26,7 @@ internal sealed class RabbitMqClient : IRabbitMqClient
2426 private readonly bool _persistMessages ;
2527 private readonly int _maxChannels ;
2628
27- private readonly ConcurrentDictionary < int , IModel > _channels = new ( ) ;
29+ private readonly ConcurrentDictionary < int , IChannel > _channels = new ( ) ;
2830
2931 private int _channelsCount ;
3032
@@ -48,21 +50,24 @@ public RabbitMqClient(
4850 _maxChannels = options . MaxProducerChannels <= 0 ? 1000 : options . MaxProducerChannels ;
4951 }
5052
51- public void Send (
53+ public async Task SendAsync (
5254 object message ,
5355 IConvention convention ,
5456 string messageId = null ,
5557 string correlationId = null ,
5658 string spanContext = null ,
5759 object messageContext = null ,
58- IDictionary < string , object > headers = null )
60+ IDictionary < string , object > headers = null ,
61+ CancellationToken cancellationToken = default )
5962 {
6063 var threadId = Environment . CurrentManagedThreadId ;
6164
6265 if ( ! _channels . TryGetValue ( threadId , out var channel ) )
6366 {
64- lock ( _lockObject )
67+ try
6568 {
69+ await _semaphore . WaitAsync ( cancellationToken ) ;
70+
6671 if ( _channelsCount >= _maxChannels )
6772 {
6873 throw new InvalidOperationException (
@@ -71,7 +76,7 @@ public void Send(
7176 "Modify `MaxProducerChannels` setting to allow more channels." ) ;
7277 }
7378
74- channel = _connection . CreateModel ( ) ;
79+ channel = await _connection . CreateChannelAsync ( cancellationToken : cancellationToken ) ;
7580 _channels . TryAdd ( threadId , channel ) ;
7681 _channelsCount ++ ;
7782
@@ -84,6 +89,10 @@ public void Send(
8489 _maxChannels ) ;
8590 }
8691 }
92+ finally
93+ {
94+ _semaphore . Release ( ) ;
95+ }
8796 }
8897 else
8998 {
@@ -97,17 +106,18 @@ public void Send(
97106 }
98107 }
99108
100- var properties = channel . CreateBasicProperties ( ) ;
101-
102- properties . AppId = _appOptions . Service ;
103- properties . ContentEncoding = _serializer . ContentEncoding ;
104- properties . ContentType = _serializer . ContentType ;
105- properties . Persistent = _persistMessages ;
106- properties . MessageId = string . IsNullOrWhiteSpace ( messageId ) ? Guid . NewGuid ( ) . ToString ( "N" ) : messageId ;
107- properties . CorrelationId = string . IsNullOrWhiteSpace ( correlationId ) ? Guid . NewGuid ( ) . ToString ( "N" ) : correlationId ;
108- properties . Timestamp = new AmqpTimestamp ( DateTimeOffset . UtcNow . ToUnixTimeSeconds ( ) ) ;
109- properties . Type = convention . Type ? . Name ;
110- properties . Headers = new Dictionary < string , object > ( ) ;
109+ var properties = new BasicProperties
110+ {
111+ AppId = _appOptions . Service ,
112+ ContentEncoding = _serializer . ContentEncoding ,
113+ ContentType = _serializer . ContentType ,
114+ Persistent = _persistMessages ,
115+ MessageId = string . IsNullOrWhiteSpace ( messageId ) ? Guid . NewGuid ( ) . ToString ( "N" ) : messageId ,
116+ CorrelationId = string . IsNullOrWhiteSpace ( correlationId ) ? Guid . NewGuid ( ) . ToString ( "N" ) : correlationId ,
117+ Timestamp = new AmqpTimestamp ( DateTimeOffset . UtcNow . ToUnixTimeSeconds ( ) ) ,
118+ Type = convention . Type ? . Name ,
119+ Headers = new Dictionary < string , object > ( )
120+ } ;
111121
112122 if ( _contextEnabled )
113123 {
@@ -144,11 +154,22 @@ public void Send(
144154
145155 var body = _serializer . Serialize ( message ) ;
146156
147- channel . BasicPublish ( convention . Exchange , convention . RoutingKey , properties , body . ToArray ( ) ) ;
157+ await channel . BasicPublishAsync (
158+ exchange : convention . Exchange ,
159+ routingKey : convention . RoutingKey ,
160+ mandatory : false ,
161+ basicProperties : properties ,
162+ body : body . ToArray ( ) ,
163+ cancellationToken : cancellationToken ) ;
148164 }
149165
150166 private void IncludeMessageContext ( object context , IBasicProperties properties )
151167 {
168+ if ( properties ? . Headers is null )
169+ {
170+ return ;
171+ }
172+
152173 if ( context is not null )
153174 {
154175 properties . Headers . Add ( _contextProvider . HeaderName , _serializer . Serialize ( context ) . ToArray ( ) ) ;
0 commit comments