Skip to content

Commit 5fac714

Browse files
authored
feat(app/inbound): introduce request duration metrics (#4432)
* feat(app/inbound): introduce request duration metrics this commit introduces a new middleware layer to the inbound proxy. this instruments inbound traffic with Prometheus telemetry that records request body latency, and emits a histogram of request body durations. as in #4420, the buckets are chosen to mimic the request and response buckets emitted by the outbound proxy, with their granularity flipped. in other words, the inbound proxy is more interested in fine-grained request body metrics than response body metrics, while the outbound proxy is more interested in fine-grained response body metrics than request body metrics. * #4420 Signed-off-by: katelyn martin <kate@buoyant.io> * refactor(app/inbound): avoid ambiguous reëxports > Doesn't this pull into scope and export both `MkLabelDuration` from `req_duration` and `rsp_duration`? - #4432 (comment) this commit avoids the potential for ambiguous reëxports by opting to refrain from `pub use` statements with wild cards. instead, we make submodules public, and update callsites (there are few) to refer to imports directly. Signed-off-by: katelyn martin <kate@buoyant.io> * nit(app/inbound): fix typo in comment #4432 (comment) Signed-off-by: katelyn martin <kate@buoyant.io> * nit(app/inbound): fix typo in comment #4432 (comment) Signed-off-by: katelyn martin <kate@buoyant.io> * refactor(app/inbound): replace import wildcards Signed-off-by: katelyn martin <kate@buoyant.io> --------- Signed-off-by: katelyn martin <kate@buoyant.io>
1 parent 2282b7d commit 5fac714

File tree

4 files changed

+150
-21
lines changed

4 files changed

+150
-21
lines changed

linkerd/app/inbound/src/http/router/metrics.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1+
use self::{
2+
count_reqs::{ExtractRequestCount, NewCountRequests},
3+
labels::RouteLabels,
4+
req_body::{ExtractRequestBodyDataParams, NewRecordRequestBodyData},
5+
req_duration::{ExtractRequestDurationMetrics, NewRequestDuration},
6+
rsp_body::{ExtractResponseBodyDataMetrics, NewRecordResponseBodyData},
7+
rsp_duration::{ExtractResponseDurationMetrics, NewResponseDuration},
8+
status::{ExtractStatusCodeParams, NewRecordStatusCode},
9+
};
110
use crate::InboundMetrics;
211
use linkerd_app_core::svc;
312

4-
pub use self::{
5-
count_reqs::*, labels::RouteLabels, req_body::*, rsp_body::*, rsp_duration::*, status::*,
6-
};
7-
8-
mod count_reqs;
9-
mod labels;
10-
mod req_body;
11-
mod rsp_body;
12-
mod rsp_duration;
13-
mod status;
13+
pub mod count_reqs;
14+
pub mod labels;
15+
pub mod req_body;
16+
pub mod req_duration;
17+
pub mod rsp_body;
18+
pub mod rsp_duration;
19+
pub mod status;
1420

1521
pub(super) fn layer<N>(
1622
InboundMetrics {
1723
request_count,
1824
request_body_data,
25+
request_duration,
1926
response_body_data,
2027
response_duration,
2128
status_codes,
@@ -34,6 +41,11 @@ pub(super) fn layer<N>(
3441
NewResponseDuration::layer_via(extract)
3542
};
3643

44+
let request_duration = {
45+
let extract = ExtractRequestDurationMetrics(request_duration.clone());
46+
NewRequestDuration::layer_via(extract)
47+
};
48+
3749
let response_body = {
3850
let extract = ExtractResponseBodyDataMetrics::new(response_body_data.clone());
3951
NewRecordResponseBodyData::layer_via(extract)
@@ -50,15 +62,17 @@ pub(super) fn layer<N>(
5062
};
5163

5264
svc::layer::mk(move |inner| {
53-
count.layer(
54-
response_duration.layer(response_body.layer(request_body.layer(status.layer(inner)))),
55-
)
65+
count.layer(response_duration.layer(
66+
request_duration.layer(response_body.layer(request_body.layer(status.layer(inner)))),
67+
))
5668
})
5769
}
5870

5971
/// An `N`-typed service instrumented with metrics middleware.
6072
type Instrumented<N> = NewCountRequests<
6173
NewResponseDuration<
62-
NewRecordResponseBodyData<NewRecordRequestBodyData<NewRecordStatusCode<N>>>,
74+
NewRequestDuration<
75+
NewRecordResponseBodyData<NewRecordRequestBodyData<NewRecordStatusCode<N>>>,
76+
>,
6377
>,
6478
>;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use super::RouteLabels;
2+
use crate::policy::PermitVariant;
3+
use linkerd_app_core::{
4+
metrics::prom::{self, EncodeLabelSetMut},
5+
svc,
6+
};
7+
use linkerd_http_prom::{
8+
record_response::{self, Params},
9+
stream_label::with::MkWithLabels,
10+
};
11+
12+
pub type NewRequestDuration<N> =
13+
record_response::NewRequestDuration<MkLabelDuration, ExtractRequestDurationMetrics, N>;
14+
15+
pub type RequestDurationParams =
16+
Params<MkLabelDuration, record_response::RequestMetrics<RequestDurationLabels>>;
17+
18+
#[derive(Clone, Debug)]
19+
pub struct ExtractRequestDurationMetrics(pub RequestDurationFamilies);
20+
21+
#[derive(Clone, Debug)]
22+
pub struct RequestDurationFamilies {
23+
grpc: record_response::RequestMetrics<RequestDurationLabels>,
24+
http: record_response::RequestMetrics<RequestDurationLabels>,
25+
}
26+
27+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
28+
pub struct RequestDurationLabels {
29+
route: RouteLabels,
30+
}
31+
32+
pub type MkLabelDuration = MkWithLabels<RequestDurationLabels>;
33+
34+
// === impl RequestDurationFamilies ===
35+
36+
impl RequestDurationFamilies {
37+
/// Registers a new [`RequestDurationFamilies`] with the given registry.
38+
pub fn register(
39+
reg: &mut prom::Registry,
40+
histo: impl Clone + IntoIterator<Item = f64>,
41+
) -> Self {
42+
let grpc = {
43+
let reg = reg.sub_registry_with_prefix("grpc");
44+
record_response::RequestMetrics::register(reg, histo.clone())
45+
};
46+
47+
let http = {
48+
let reg = reg.sub_registry_with_prefix("http");
49+
record_response::RequestMetrics::register(reg, histo)
50+
};
51+
52+
Self { grpc, http }
53+
}
54+
}
55+
56+
// === impl ExtractRequestDurationMetrics ===
57+
58+
impl<T> svc::ExtractParam<RequestDurationParams, T> for ExtractRequestDurationMetrics
59+
where
60+
T: svc::Param<PermitVariant> + svc::Param<RouteLabels>,
61+
{
62+
fn extract_param(&self, target: &T) -> RequestDurationParams {
63+
let Self(families) = self;
64+
65+
let labeler = {
66+
let route: RouteLabels = target.param();
67+
let labels = RequestDurationLabels { route };
68+
MkLabelDuration::new(labels)
69+
};
70+
71+
let metric = {
72+
let variant: PermitVariant = target.param();
73+
let RequestDurationFamilies { grpc, http } = families;
74+
match variant {
75+
PermitVariant::Grpc => grpc,
76+
PermitVariant::Http => http,
77+
}
78+
.clone()
79+
};
80+
81+
RequestDurationParams { labeler, metric }
82+
}
83+
}
84+
85+
// === impl RequestDurationLabels ===
86+
87+
impl prom::EncodeLabelSetMut for RequestDurationLabels {
88+
fn encode_label_set(
89+
&self,
90+
encoder: &mut prom::encoding::LabelSetEncoder<'_>,
91+
) -> std::fmt::Result {
92+
let Self { route } = self;
93+
route.encode_label_set(encoder)?;
94+
Ok(())
95+
}
96+
}
97+
98+
impl prom::encoding::EncodeLabelSet for RequestDurationLabels {
99+
fn encode(&self, mut encoder: prom::encoding::LabelSetEncoder<'_>) -> std::fmt::Result {
100+
self.encode_label_set(&mut encoder)
101+
}
102+
}

linkerd/app/inbound/src/metrics.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ pub(crate) mod authz;
1212
pub(crate) mod error;
1313

1414
use crate::http::router::metrics::{
15-
RequestBodyFamilies, RequestCountFamilies, ResponseBodyFamilies, ResponseDurationFamilies,
16-
StatusCodeFamilies,
15+
count_reqs::RequestCountFamilies, req_body::RequestBodyFamilies,
16+
req_duration::RequestDurationFamilies, rsp_body::ResponseBodyFamilies,
17+
rsp_duration::ResponseDurationFamilies, status::StatusCodeFamilies,
1718
};
19+
1820
pub use linkerd_app_core::metrics::*;
1921

2022
/// Holds LEGACY inbound proxy metrics.
@@ -34,6 +36,7 @@ pub struct InboundMetrics {
3436
pub direct: crate::direct::MetricsFamilies,
3537
pub request_count: RequestCountFamilies,
3638
pub request_body_data: RequestBodyFamilies,
39+
pub request_duration: RequestDurationFamilies,
3740
pub response_body_data: ResponseBodyFamilies,
3841
pub response_duration: ResponseDurationFamilies,
3942
pub status_codes: StatusCodeFamilies,
@@ -48,6 +51,8 @@ impl InboundMetrics {
4851
);
4952
let request_count = RequestCountFamilies::register(reg);
5053
let request_body_data = RequestBodyFamilies::register(reg);
54+
let request_duration =
55+
RequestDurationFamilies::register(reg, Self::REQUEST_BUCKETS.iter().copied());
5156
let response_body_data = ResponseBodyFamilies::register(reg);
5257
let response_duration =
5358
ResponseDurationFamilies::register(reg, Self::RESPONSE_BUCKETS.iter().copied());
@@ -63,24 +68,32 @@ impl InboundMetrics {
6368
direct,
6469
request_count,
6570
request_body_data,
71+
request_duration,
6672
response_body_data,
6773
response_duration,
6874
status_codes,
6975
}
7076
}
7177

7278
// There are two histograms for which we need to register metrics:
73-
// (1) request durations, which are measured on routes. TODO(kate): forthcoming.
79+
// (1) request durations, which are measured on routes.
7480
// (2) response durations, which are measured on route-backends.
7581
//
7682
// Should these change in the future, be sure to consider the outbound proxy's corresponding
7783
// constants measuring request and response latency for *outgoing* traffic.
7884

85+
/// Histogram buckets for request latency.
86+
///
87+
/// Because request duration is the more meaningful metric operationally for the inbound
88+
/// proxy, we opt to preserve higher fidelity for request durations (especially for lower
89+
/// values).
90+
const REQUEST_BUCKETS: &'static [f64] = &[0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 10.0];
91+
7992
/// Histogram buckets for response latency.
8093
///
81-
/// These buckets for this histogram are coarse, eliding several buckets for short response
82-
/// durations to be conservative about the costs of tracking two histograms' respective time
83-
/// series.
94+
/// These buckets for this histogram are coarser than those of [`Self::REQUEST_BUCKETS`],
95+
/// eliding several buckets for short response durations to be conservative about the costs of
96+
/// tracking two histograms' respective time series.
8497
const RESPONSE_BUCKETS: &'static [f64] = &[0.05, 0.5, 1.0, 10.0];
8598
}
8699

linkerd/app/inbound/src/policy/http.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{RoutePolicy, Routes};
22
use crate::{
3-
http::router::metrics::RouteLabels,
3+
http::router::metrics::labels::RouteLabels,
44
metrics::authz::HttpAuthzMetrics,
55
policy::{AllowPolicy, HttpRoutePermit},
66
};

0 commit comments

Comments
 (0)