Skip to content

Commit 9d0c851

Browse files
committed
Show session and link details for AMQP 1.0 connection
## What? On the connection page in the Management UI, display detailed session and link information including: * Link names * Link target and source addresses * Link flow control state * Session flow control state * Number of unconfirmed and unacknowledged messages ## How? A new HTTP API endpoint is added: ``` /connections/:connection_name/sessions ``` The HTTP handler first queries the Erlang connection process to find out about all session Pids. The handler then queries each Erlang session process of this connection. (The table auto-refreshes by default every 5 seconds. The handler querying a single connection with 60 idle sessions with each 250 links takes ~100 ms.) For better user experience in the Management UI, this commit also makes the session process store and expose link names as well as source/target addresses.
1 parent 8dd49a5 commit 9d0c851

File tree

13 files changed

+553
-37
lines changed

13 files changed

+553
-37
lines changed

deps/rabbit/src/rabbit_amqp_reader.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,8 @@ silent_close_delay() ->
967967
-spec info(rabbit_types:connection(), rabbit_types:info_keys()) ->
968968
rabbit_types:infos().
969969
info(Pid, InfoItems) ->
970-
case InfoItems -- ?INFO_ITEMS of
970+
KnownItems = [session_pids | ?INFO_ITEMS],
971+
case InfoItems -- KnownItems of
971972
[] ->
972973
case gen_server:call(Pid, {info, InfoItems}, infinity) of
973974
{ok, InfoList} ->
@@ -1065,6 +1066,8 @@ i(client_properties, #v1{connection = #v1_connection{properties = Props}}) ->
10651066
end;
10661067
i(channels, #v1{tracked_channels = Channels}) ->
10671068
maps:size(Channels);
1069+
i(session_pids, #v1{tracked_channels = Map}) ->
1070+
maps:values(Map);
10681071
i(channel_max, #v1{connection = #v1_connection{channel_max = Max}}) ->
10691072
Max;
10701073
i(reductions = Item, _State) ->

deps/rabbit/src/rabbit_amqp_session.erl

Lines changed: 134 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@
9292
conserve_resources/3,
9393
check_resource_access/4,
9494
check_read_permitted_on_topic/4,
95-
reset_authz/2
95+
reset_authz/2,
96+
info/1
9697
]).
9798

9899
-export([init/1,
@@ -148,7 +149,9 @@
148149
}).
149150

150151
-record(incoming_link, {
152+
name :: binary(),
151153
snd_settle_mode :: snd_settle_mode(),
154+
target_address :: null | binary(),
152155
%% The exchange is either defined in the ATTACH frame and static for
153156
%% the life time of the link or dynamically provided in each message's
154157
%% "to" field (address v2).
@@ -197,6 +200,8 @@
197200
}).
198201

199202
-record(outgoing_link, {
203+
name :: binary(),
204+
source_address :: binary(),
200205
%% Although the source address of a link might be an exchange name and binding key
201206
%% or a topic filter, an outgoing link will always consume from a queue.
202207
queue_name :: rabbit_amqqueue:name(),
@@ -490,6 +495,8 @@ conserve_resources(Pid, Source, {_, Conserve, _}) ->
490495
reset_authz(Pid, User) ->
491496
gen_server:cast(Pid, {reset_authz, User}).
492497

498+
handle_call(infos, _From, State) ->
499+
reply(infos(State), State);
493500
handle_call(Msg, _From, State) ->
494501
Reply = {error, {not_understood, Msg}},
495502
reply(Reply, State).
@@ -1262,11 +1269,11 @@ handle_attach(#'v1_0.attach'{
12621269
reply_frames([Reply], State);
12631270

12641271
handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_SENDER,
1265-
name = LinkName,
1272+
name = LinkName = {utf8, LinkName0},
12661273
handle = Handle = ?UINT(HandleInt),
12671274
source = Source,
12681275
snd_settle_mode = MaybeSndSettleMode,
1269-
target = Target,
1276+
target = Target = #'v1_0.target'{address = TargetAddress},
12701277
initial_delivery_count = DeliveryCount = ?UINT(DeliveryCountInt)
12711278
},
12721279
State0 = #state{incoming_links = IncomingLinks0,
@@ -1279,7 +1286,9 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_SENDER,
12791286
SndSettleMode = snd_settle_mode(MaybeSndSettleMode),
12801287
MaxMessageSize = persistent_term:get(max_message_size),
12811288
IncomingLink = #incoming_link{
1289+
name = LinkName0,
12821290
snd_settle_mode = SndSettleMode,
1291+
target_address = address(TargetAddress),
12831292
exchange = Exchange,
12841293
routing_key = RoutingKey,
12851294
queue_name_bin = QNameBin,
@@ -1316,9 +1325,10 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_SENDER,
13161325
end;
13171326

13181327
handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_RECEIVER,
1319-
name = LinkName,
1328+
name = LinkName = {utf8, LinkName0},
13201329
handle = Handle = ?UINT(HandleInt),
1321-
source = Source = #'v1_0.source'{filter = DesiredFilter},
1330+
source = Source = #'v1_0.source'{address = SourceAddress,
1331+
filter = DesiredFilter},
13221332
snd_settle_mode = SndSettleMode,
13231333
rcv_settle_mode = RcvSettleMode,
13241334
max_message_size = MaybeMaxMessageSize,
@@ -1431,6 +1441,8 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_RECEIVER,
14311441
offered_capabilities = OfferedCaps},
14321442
MaxMessageSize = max_message_size(MaybeMaxMessageSize),
14331443
Link = #outgoing_link{
1444+
name = LinkName0,
1445+
source_address = address(SourceAddress),
14341446
queue_name = queue_resource(Vhost, QNameBin),
14351447
queue_type = QType,
14361448
send_settled = SndSettled,
@@ -2672,6 +2684,11 @@ ensure_source_v1(Address,
26722684
Err
26732685
end.
26742686

2687+
address(undefined) ->
2688+
null;
2689+
address({utf8, String}) ->
2690+
String.
2691+
26752692
-spec ensure_target(#'v1_0.target'{},
26762693
rabbit_types:vhost(),
26772694
rabbit_types:user(),
@@ -3702,6 +3719,118 @@ format_status(
37023719
topic_permission_cache => TopicPermissionCache},
37033720
maps:update(state, State, Status).
37043721

3722+
-spec info(pid()) ->
3723+
{ok, rabbit_types:infos()} | {error, term()}.
3724+
info(Pid) ->
3725+
try gen_server:call(Pid, infos) of
3726+
Infos ->
3727+
{ok, Infos}
3728+
catch _:Reason ->
3729+
{error, Reason}
3730+
end.
3731+
3732+
infos(#state{cfg = #cfg{channel_num = ChannelNum,
3733+
max_handle = MaxHandle},
3734+
next_incoming_id = NextIncomingId,
3735+
incoming_window = IncomingWindow,
3736+
next_outgoing_id = NextOutgoingId,
3737+
remote_incoming_window = RemoteIncomingWindow,
3738+
remote_outgoing_window = RemoteOutgoingWindow,
3739+
outgoing_unsettled_map = OutgoingUnsettledMap,
3740+
incoming_links = IncomingLinks,
3741+
outgoing_links = OutgoingLinks,
3742+
incoming_management_links = IncomingManagementLinks,
3743+
outgoing_management_links = OutgoingManagementLinks
3744+
}) ->
3745+
[
3746+
{channel_number, ChannelNum},
3747+
{handle_max, MaxHandle},
3748+
{next_incoming_id, NextIncomingId},
3749+
{incoming_window, IncomingWindow},
3750+
{next_outgoing_id, NextOutgoingId},
3751+
{remote_incoming_window, RemoteIncomingWindow},
3752+
{remote_outgoing_window, RemoteOutgoingWindow},
3753+
{outgoing_unsettled_deliveries, maps:size(OutgoingUnsettledMap)},
3754+
{incoming_links,
3755+
info_incoming_management_links(IncomingManagementLinks) ++
3756+
info_incoming_links(IncomingLinks)},
3757+
{outgoing_links,
3758+
info_outgoing_management_links(OutgoingManagementLinks) ++
3759+
info_outgoing_links(OutgoingLinks)}
3760+
].
3761+
3762+
info_incoming_management_links(Links) ->
3763+
[info_incoming_link(Handle, Name, settled, ?MANAGEMENT_NODE_ADDRESS,
3764+
MaxMessageSize, DeliveryCount, Credit, 0)
3765+
|| Handle := #management_link{
3766+
name = Name,
3767+
max_message_size = MaxMessageSize,
3768+
delivery_count = DeliveryCount,
3769+
credit = Credit} <- Links].
3770+
3771+
info_incoming_links(Links) ->
3772+
[info_incoming_link(Handle, Name, SndSettleMode, TargetAddress, MaxMessageSize,
3773+
DeliveryCount, Credit, maps:size(IncomingUnconfirmedMap))
3774+
|| Handle := #incoming_link{
3775+
name = Name,
3776+
snd_settle_mode = SndSettleMode,
3777+
target_address = TargetAddress,
3778+
max_message_size = MaxMessageSize,
3779+
delivery_count = DeliveryCount,
3780+
credit = Credit,
3781+
incoming_unconfirmed_map = IncomingUnconfirmedMap} <- Links].
3782+
3783+
info_incoming_link(Handle, LinkName, SndSettleMode, TargetAddress,
3784+
MaxMessageSize, DeliveryCount, Credit, UnconfirmedMessages) ->
3785+
[{handle, Handle},
3786+
{link_name, LinkName},
3787+
{snd_settle_mode, SndSettleMode},
3788+
{target_address, TargetAddress},
3789+
{max_message_size, MaxMessageSize},
3790+
{delivery_count, DeliveryCount},
3791+
{credit, Credit},
3792+
{unconfirmed_messages, UnconfirmedMessages}].
3793+
3794+
info_outgoing_management_links(Links) ->
3795+
[info_outgoing_link(Handle, Name, ?MANAGEMENT_NODE_ADDRESS, <<>>,
3796+
true, MaxMessageSize, DeliveryCount, Credit)
3797+
|| Handle := #management_link{
3798+
name = Name,
3799+
max_message_size = MaxMessageSize,
3800+
delivery_count = DeliveryCount,
3801+
credit = Credit} <- Links].
3802+
3803+
info_outgoing_links(Links) ->
3804+
[begin
3805+
{DeliveryCount, Credit} = case ClientFlowCtl of
3806+
#client_flow_ctl{delivery_count = DC,
3807+
credit = C} ->
3808+
{DC, C};
3809+
credit_api_v1 ->
3810+
{'', ''}
3811+
end,
3812+
info_outgoing_link(Handle, Name, SourceAddress, QueueName#resource.name,
3813+
SendSettled, MaxMessageSize, DeliveryCount, Credit)
3814+
3815+
end
3816+
|| Handle := #outgoing_link{
3817+
name = Name,
3818+
source_address = SourceAddress,
3819+
queue_name = QueueName,
3820+
max_message_size = MaxMessageSize,
3821+
send_settled = SendSettled,
3822+
client_flow_ctl = ClientFlowCtl} <- Links].
3823+
3824+
info_outgoing_link(Handle, LinkName, SourceAddress, QueueNameBin, SendSettled,
3825+
MaxMessageSize, DeliveryCount, Credit) ->
3826+
[{handle, Handle},
3827+
{link_name, LinkName},
3828+
{source_address, SourceAddress},
3829+
{queue_name, QueueNameBin},
3830+
{send_settled, SendSettled},
3831+
{max_message_size, MaxMessageSize},
3832+
{delivery_count, DeliveryCount},
3833+
{credit, Credit}].
37053834

37063835
unwrap_simple_type(V = {list, _}) ->
37073836
V;

deps/rabbitmq_management/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ define PROJECT_APP_EXTRA_KEYS
2222
endef
2323

2424
DEPS = rabbit_common rabbit amqp_client cowboy cowlib rabbitmq_web_dispatch rabbitmq_management_agent oauth2_client
25-
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper amqp10_client
25+
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper rabbitmq_amqp_client
2626
LOCAL_DEPS += ranch ssl crypto public_key
2727

2828
# FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.

deps/rabbitmq_management/app.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def all_beam_files(name = "all_beam_files"):
4848
"src/rabbit_mgmt_wm_cluster_name.erl",
4949
"src/rabbit_mgmt_wm_connection.erl",
5050
"src/rabbit_mgmt_wm_connection_channels.erl",
51+
"src/rabbit_mgmt_wm_connection_sessions.erl",
5152
"src/rabbit_mgmt_wm_connection_user_name.erl",
5253
"src/rabbit_mgmt_wm_connections.erl",
5354
"src/rabbit_mgmt_wm_connections_vhost.erl",
@@ -182,6 +183,7 @@ def all_test_beam_files(name = "all_test_beam_files"):
182183
"src/rabbit_mgmt_wm_cluster_name.erl",
183184
"src/rabbit_mgmt_wm_connection.erl",
184185
"src/rabbit_mgmt_wm_connection_channels.erl",
186+
"src/rabbit_mgmt_wm_connection_sessions.erl",
185187
"src/rabbit_mgmt_wm_connection_user_name.erl",
186188
"src/rabbit_mgmt_wm_connections.erl",
187189
"src/rabbit_mgmt_wm_connections_vhost.erl",
@@ -361,6 +363,7 @@ def all_srcs(name = "all_srcs"):
361363
"priv/www/js/tmpl/queues.ejs",
362364
"priv/www/js/tmpl/rate-options.ejs",
363365
"priv/www/js/tmpl/registry.ejs",
366+
"priv/www/js/tmpl/sessions-list.ejs",
364367
"priv/www/js/tmpl/status.ejs",
365368
"priv/www/js/tmpl/topic-permissions.ejs",
366369
"priv/www/js/tmpl/user.ejs",
@@ -407,6 +410,7 @@ def all_srcs(name = "all_srcs"):
407410
"src/rabbit_mgmt_wm_cluster_name.erl",
408411
"src/rabbit_mgmt_wm_connection.erl",
409412
"src/rabbit_mgmt_wm_connection_channels.erl",
413+
"src/rabbit_mgmt_wm_connection_sessions.erl",
410414
"src/rabbit_mgmt_wm_connection_user_name.erl",
411415
"src/rabbit_mgmt_wm_connections.erl",
412416
"src/rabbit_mgmt_wm_connections_vhost.erl",

deps/rabbitmq_management/priv/www/js/dispatcher.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,21 @@ dispatcher_add(function(sammy) {
4646
});
4747
sammy.get('#/connections/:name', function() {
4848
var name = esc(this.params['name']);
49-
render({'connection': {path: '/connections/' + name,
50-
options: {ranges: ['data-rates-conn']}},
51-
'channels': '/connections/' + name + '/channels'},
52-
'connection', '#/connections');
49+
var connectionPath = '/connections/' + name;
50+
var reqs = {
51+
'connection': {
52+
path: connectionPath,
53+
options: { ranges: ['data-rates-conn'] }
54+
}
55+
};
56+
// First, get the connection details to check the protocol
57+
var connectionDetails = JSON.parse(sync_get(connectionPath));
58+
if (connectionDetails.protocol === 'AMQP 1-0') {
59+
reqs['sessions'] = connectionPath + '/sessions';
60+
} else {
61+
reqs['channels'] = connectionPath + '/channels';
62+
}
63+
render(reqs, 'connection', '#/connections');
5364
});
5465
sammy.del('#/connections', function() {
5566
var options = {headers: {

deps/rabbitmq_management/priv/www/js/global.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,34 @@ var HELP = {
586586
</dl> ',
587587

588588
'container-id':
589-
'Name of the client application as sent from client to RabbitMQ in the "container-id" field of the AMQP 1.0 <a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-open">open</a> frame.'
589+
'Name of the client application as sent from client to RabbitMQ in the "container-id" field of the AMQP 1.0 <a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-open">open</a> frame.',
590590

591+
'incoming-links':
592+
'<a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#section-links">Links</a> where the client is the sender/publisher and RabbitMQ is the receiver of messages.',
593+
594+
'outgoing-links':
595+
'<a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#section-links">Links</a> where the client is the receiver/consumer and RabbitMQ is the sender of messages.',
596+
597+
'target-address':
598+
'The "address" field of the link <a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-target">target</a>.',
599+
600+
'source-address':
601+
'The "address" field of the link <a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-source">source</a>.',
602+
603+
'amqp-source-queue':
604+
'The client receives messages from this queue.',
605+
606+
'amqp-unconfirmed-messages':
607+
'Number of messages that have been sent to queues but have not been confirmed by all queues.',
608+
609+
'snd-settle-mode':
610+
'<a target="_blank" href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-sender-settle-mode">Sender Settle Mode</a>',
611+
612+
'sender-settles':
613+
'"true" if the sender sends all deliveries settled to the receiver. "false" if the sender sends all deliveries initially unsettled to the receiver.',
614+
615+
'outgoing-unsettled-deliveries':
616+
'Number of messages that have been sent to consumers but have not yet been settled/acknowledged.'
591617
};
592618

593619
///////////////////////////////////////////////////////////////////////////

deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,26 @@
8484
</div>
8585
</div>
8686
87+
<% if (connection.protocol === 'AMQP 1-0') { %>
88+
89+
<div class="section">
90+
<h2 class="updatable" >Sessions (<%=(sessions.length)%>)</h2>
91+
<div class="hider updatable">
92+
<%= format('sessions-list', {'sessions': sessions}) %>
93+
</div>
94+
</div>
95+
96+
<% } else { %>
97+
8798
<div class="section">
8899
<h2 class="updatable" >Channels (<%=(channels.length)%>) </h2>
89100
<div class="hider updatable">
90101
<%= format('channels-list', {'channels': channels, 'mode': 'connection'}) %>
91102
</div>
92103
</div>
93104
105+
<% } %>
106+
94107
<% if (connection.ssl) { %>
95108
<div class="section">
96109
<h2>SSL</h2>

0 commit comments

Comments
 (0)