@@ -164,9 +164,19 @@ const CONN_STATE_OPENING = 1
164
164
const CONN_STATE_OPEN = 2
165
165
const CONN_STATE_CLOSING = 3
166
166
const CONN_MAX_QUEUED = 1024 # typemax(Int)
167
+ const DEFAULT_KEEPALIVE_SECS = 60
167
168
168
169
abstract type AbstractChannel end
169
170
171
+ function keepalive! (sock, enable:: Bool ; interval:: Integer = DEFAULT_KEEPALIVE_SECS)
172
+ @debug (" setting tcp keepalive on tcp socket" , enable, interval)
173
+ err = ccall (:uv_tcp_keepalive , Cint, (Ptr{Nothing}, Cint, Cuint), sock. handle, enable, interval)
174
+ if err != 0
175
+ throw (AMQPProtocolException (" error setting keepalive on socket to $enable with interval $interval " ))
176
+ end
177
+ return sock
178
+ end
179
+
170
180
mutable struct Connection
171
181
virtualhost:: String
172
182
host:: String
@@ -178,6 +188,9 @@ mutable struct Connection
178
188
channelmax:: TAMQPShortInt
179
189
framemax:: TAMQPLongInt
180
190
heartbeat:: TAMQPShortInt
191
+ enable_heartbeat:: Bool
192
+ keepalive:: Integer
193
+ enable_keepalive:: Bool
181
194
182
195
state:: UInt8
183
196
sendq:: Channel{TAMQPGenericFrame}
@@ -191,12 +204,22 @@ mutable struct Connection
191
204
heartbeat_time_server:: Float64
192
205
heartbeat_time_client:: Float64
193
206
194
- function Connection (; virtualhost:: String = " /" , host:: String = " localhost" , port:: Int = AMQP_DEFAULT_PORT, send_queue_size:: Int = CONN_MAX_QUEUED)
207
+ function Connection (;
208
+ virtualhost:: String = " /" ,
209
+ host:: String = " localhost" ,
210
+ port:: Int = AMQP_DEFAULT_PORT,
211
+ send_queue_size:: Int = CONN_MAX_QUEUED,
212
+ heartbeat:: Integer = 0 ,
213
+ enable_heartbeat:: Bool = true ,
214
+ keepalive:: Integer = DEFAULT_KEEPALIVE_SECS,
215
+ enable_keepalive:: Bool = true ,
216
+ )
195
217
sendq = Channel {TAMQPGenericFrame} (send_queue_size)
196
218
sendlck = Channel {UInt8} (1 )
197
219
put! (sendlck, 1 )
198
220
new (virtualhost, host, port, nothing ,
199
- Dict {Symbol,Any} (), Dict {String,Any} (), 0 , 0 , 0 ,
221
+ Dict {Symbol,Any} (), Dict {String,Any} (), 0 , 0 ,
222
+ heartbeat, enable_heartbeat, keepalive, enable_keepalive,
200
223
CONN_STATE_CLOSED, sendq, sendlck, Dict {TAMQPChannel, AbstractChannel} (),
201
224
nothing , nothing , nothing ,
202
225
0.0 , 0.0 )
@@ -502,6 +525,21 @@ function find_unused_channel(c::Connection)
502
525
end
503
526
throw (AMQPClientException (" No free channel available (max: $maxid )" ))
504
527
end
528
+
529
+ """
530
+ channel(conn, id, create)
531
+ channel(f, args...)
532
+
533
+ Create or return an existing a channel object.
534
+ Multiple channels can be multiplexed over a single connection.
535
+ Can be used with the Julia do block syntax to create a channel and close it afterwards.
536
+
537
+ - `conn`: The connection over which to create the channel.
538
+ - `id`: Channels are identified by their numeric id. Specifying `AMQPClient.UNUSED_CHANNEL` as channel
539
+ id during creation will automatically assign an unused id.
540
+ - `create`: If true, a new channel will be created. Else an existing channel with the specified id
541
+ will be returned.
542
+ """
505
543
channel (c:: MessageChannel , id:: Integer ) = channel (c. conn, id)
506
544
channel (c:: Connection , id:: Integer ) = c. channels[id]
507
545
channel (c:: MessageChannel , id:: Integer , create:: Bool ) = channel (c. conn, id, create)
@@ -546,26 +584,83 @@ function channel(f, args...; kwargs...)
546
584
end
547
585
end
548
586
587
+ """
588
+ connection(f; kwargs...)
589
+
590
+ connection(;
591
+ virtualhost = "/",
592
+ host = "localhost",
593
+ port = AMQPClient.AMQP_DEFAULT_PORT,
594
+ framemax = 0,
595
+ heartbeat = true,
596
+ keepalive = DEFAULT_KEEPALIVE_SECS,
597
+ send_queue_size = CONN_MAX_QUEUED,
598
+ auth_params = AMQPClient.DEFAULT_AUTH_PARAMS,
599
+ channelmax = AMQPClient.DEFAULT_CHANNELMAX,
600
+ connect_timeout = AMQPClient.DEFAULT_CONNECT_TIMEOUT,
601
+ amqps = nothing
602
+ )
603
+
604
+ Creates a fresh connection to the AMQP server.
605
+ Returns a connection that can be used to open channels subsequently.
606
+ Can be used with the Julia do block syntax to create a connection and close it afterwards.
607
+
608
+ Keyword arguments:
609
+ - `host`: The message server host to connect to. Defaults to "localhost".
610
+ - `port`: The message server port to connect to. Defaults to the default AMQP port.
611
+ - `virtualhost`: The virtual host to connect to. Defaults to "/".
612
+ - `amqps`: If connection is to be done over AMQPS, the TLS options to use. See `amqps_configure`.
613
+ - `connect_timeout`: TCP connect timeout to impose. Default `AMQPClient.DEFAULT_CONNECT_TIMEOUT`,
614
+ - `framemax`: The maximum frame size to use. Defaults to 0, which means no limit.
615
+ - `heartbeat`: `true` to enable heartbeat, `false` to disable. Can also be set to a positive integer,
616
+ in which case it is the heartbeat interval in seconds. Defaults to `true`. If `false`, ensure
617
+ `keepalive` is enabled to detect dead connections. This parameter is negotiated with the server.
618
+ - `keepalive`: `true` to enable TCP keepalives, `false` to disable. Can also be set to a positive integer,
619
+ in which case it is the keepalive interval in seconds. Defaults to `DEFAULT_KEEPALIVE_SECS`.
620
+ - `send_queue_size`: Maximum number of items to buffer in memory before blocking the send API until
621
+ messages are drained. Defaults to CONN_MAX_QUEUED.
622
+ - `auth_params`: Parameters to use to authenticate the connection. Defaults to AMQPClient.DEFAULT_AUTH_PARAMS.
623
+ - `channelmax`: Maximum channel number to impose/negotiate with the server. Defaults to AMQPClient.DEFAULT_CHANNELMAX.
624
+
625
+ """
549
626
function connection (; virtualhost= " /" , host= " localhost" , port= AMQPClient. AMQP_DEFAULT_PORT,
550
627
framemax= 0 ,
551
- heartbeat= 0 ,
628
+ heartbeat:: Union{Int,Bool} = true ,
629
+ keepalive:: Union{Int,Bool} = DEFAULT_KEEPALIVE_SECS,
552
630
send_queue_size:: Integer = CONN_MAX_QUEUED,
553
631
auth_params= AMQPClient. DEFAULT_AUTH_PARAMS,
554
632
channelmax:: Integer = AMQPClient. DEFAULT_CHANNELMAX,
555
633
connect_timeout= AMQPClient. DEFAULT_CONNECT_TIMEOUT,
556
634
amqps:: Union{MbedTLS.SSLConfig,Nothing} = nothing )
557
635
@debug (" connecting" , host, port, virtualhost)
558
- conn = Connection (; virtualhost= virtualhost, host= host, port= port, send_queue_size= send_queue_size)
636
+
637
+ keepalive_interval = isa (keepalive, Bool) ? DEFAULT_KEEPALIVE_SECS : keepalive
638
+ enable_keepalive = isa (keepalive, Bool) ? keepalive : (keepalive_interval > 0 )
639
+
640
+ heartbeat_interval = isa (heartbeat, Bool) ? 0 : heartbeat
641
+ enable_heartbeat = isa (heartbeat, Bool) ? heartbeat : (heartbeat > 0 )
642
+
643
+ conn = Connection (;
644
+ virtualhost= virtualhost,
645
+ host= host,
646
+ port= port,
647
+ send_queue_size= send_queue_size,
648
+ heartbeat= heartbeat_interval,
649
+ enable_heartbeat= enable_heartbeat,
650
+ keepalive= keepalive_interval,
651
+ enable_keepalive= enable_keepalive,)
559
652
chan = channel (conn, AMQPClient. DEFAULT_CHANNEL, true )
560
653
561
654
# setup handler for Connection.Start
562
- ctx = Dict (:auth_params => auth_params, :channelmax => channelmax, :framemax => framemax, :heartbeat => heartbeat )
655
+ ctx = Dict (:auth_params => auth_params, :channelmax => channelmax, :framemax => framemax, :heartbeat => heartbeat_interval )
563
656
AMQPClient. handle (chan, :Connection , :Start , AMQPClient. on_connection_start, ctx)
564
657
565
658
# open socket and start processor tasks
566
659
sock = connect (conn. host, conn. port)
567
660
isdefined (Sockets, :nagle ) && Sockets. nagle (sock, false )
568
661
isdefined (Sockets, :quickack ) && Sockets. quickack (sock, true )
662
+ keepalive! (sock, enable_keepalive; interval= keepalive_interval)
663
+
569
664
conn. sock = (amqps != = nothing ) ? setup_tls (sock, host, amqps) : sock
570
665
conn. sender = @async AMQPClient. connection_processor (conn, " ConnectionSender" , AMQPClient. connection_sender)
571
666
conn. receiver = @async AMQPClient. connection_processor (conn, " ConnectionReceiver" , AMQPClient. connection_receiver)
@@ -1119,13 +1214,15 @@ function send_connection_tune_ok(chan::MessageChannel, channelmax=0, framemax=0,
1119
1214
1120
1215
conn. channelmax = opt (channelmax, conn. channelmax)
1121
1216
conn. framemax = opt (framemax, conn. framemax)
1122
- conn. heartbeat = opt (heartbeat, conn. heartbeat)
1217
+ conn. heartbeat = conn . enable_heartbeat ? opt (heartbeat, conn. heartbeat) : 0
1123
1218
1124
1219
@debug (" send_connection_tune_ok" , channelmax= conn. channelmax, framemax= conn. framemax, heartbeat= conn. heartbeat)
1125
1220
send (chan, TAMQPMethodPayload (:Connection , :TuneOk , (conn. channelmax, conn. framemax, conn. heartbeat)))
1126
1221
1127
- # start heartbeat timer
1128
- conn. heartbeater = @async connection_processor (conn, " HeartBeater" , connection_heartbeater)
1222
+ if conn. enable_heartbeat
1223
+ # start heartbeat timer
1224
+ conn. heartbeater = @async connection_processor (conn, " HeartBeater" , connection_heartbeater)
1225
+ end
1129
1226
nothing
1130
1227
end
1131
1228
0 commit comments