New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ServerMetrics to collect metrics related server #5627
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, @seongbeenkim! Left some suggestions. 😉
sb.service(PATH, new HttpService() { | ||
@Override | ||
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception { | ||
assertThat(server.server().config().serverMetrics().activeRequests()).isOne(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also add test cases that fail. e.g. throwing an exception in this serve()
method and request timeout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i've added the test cases!!
@@ -574,14 +575,19 @@ private ChannelFuture doStart(ServerPort port) { | |||
} | |||
|
|||
private void setupServerMetrics() { | |||
final MeterRegistry meterRegistry = config().meterRegistry(); | |||
final MeterRegistry meterRegistry = config.meterRegistry(); | |||
final ServerMetrics serverMetrics = config.serverMetrics(); | |||
final GracefulShutdownSupport gracefulShutdownSupport = this.gracefulShutdownSupport; | |||
assert gracefulShutdownSupport != null; | |||
|
|||
meterRegistry.gauge("armeria.server.pending.responses", gracefulShutdownSupport, | |||
GracefulShutdownSupport::pendingResponses); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@seongbeenkim is going to add the metric to the ServerMetrics
in the follow-up PR.
/** | ||
* Returns the number of active requests. | ||
*/ | ||
public long activeRequests() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are your thoughts on breaking down activeRequests
into activeHttp1Requests
, activeHttp1WebSocketRequests
, and activeHttp2Requests
, and then summing them to form activeRequests
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think that is a great idea!
so i added activeHttp1Requests
, activeHttp1WebSocketRequests
, and activeHttp2Request
for activeRequests
!
also pendingHttp1Requests
and pendingHttp2Requests
for pendingRequests
🔍 Build Scan® (commit: 3bfbdc7) |
if (isHttp1WebSocket) { | ||
serverMetrics.increaseActiveHttp1WebSocketRequests(); | ||
serverMetrics.decreasePendingHttp1Requests(); | ||
} else if (reqCtx.sessionProtocol().isExplicitHttp1()) { | ||
serverMetrics.increaseActiveHttp1Requests(); | ||
serverMetrics.decreasePendingHttp1Requests(); | ||
} else if (reqCtx.sessionProtocol().isExplicitHttp2()) { | ||
serverMetrics.increaseActiveHttp2Requests(); | ||
serverMetrics.decreasePendingHttp2Requests(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I think the current approach looks a bit unappealing. I've changed my mind. 😓 How do you feel about incorporating this logic into the decoded request instead?
class AggregatingDecodedHttpRequest {
AggregatingDecodedHttpRequest(...) {
...
if (routingCtx.sessionProtocol().isMultiplex()) {
metrics.increasePendingHttp2Requests();
} else {
metrics.increasePendingHttp1Requests();
}
}
...
private void completeAggregationFuture() {
if (aggregationFuture.complete(null)) {
if (routingCtx.sessionProtocol().isMultiplex()) {
metrics.decreasePendingHttp2Requests();
metrics.increaseActiveHttp2Requests();
} else {
metrics.decreasePendingHttp1Requests();
metrics.decreaseActiveHttp1Requests();
}
}
}
}
class StreamingDecodedHttpRequest {
StreamingDecodedHttpRequest(...) {
...
if (http1WebSocket) {
metrics.increaseActiveHttp1WebSocketRequests();
} else if (routingCtx.sessionProtocol().isMultiplex()) {
metrics.increaseActiveHttp2Requests();
} else {
metrics.increaseActiveHttp1Requests();
}
}
}
class EmptyContentDecodedHttpRequest {...}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm so sorry to take it so long to apply the review!
i've been trying to write more test cases related to AggregatingDecodedHttpRequest, StreamingDecodedHttpRequest and StreamingDecodedHttpRequest.
but it's a little bit hard and taking some time..
i will do my best to apply the review asap!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall! Left some minor comments
@@ -44,13 +50,18 @@ void testExceedMaxNumConnections() { | |||
|
|||
@Test | |||
void testMaxNumConnectionsRange() { | |||
final ConnectionLimitingHandler handler = new ConnectionLimitingHandler(Integer.MAX_VALUE); | |||
final Server server = Server.builder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question) Couldn't we just create a ServerMetrics
instance and pass it instead of creating a Server
?
Ditto for the other test case above.
e.g.
final ServerMetrics serverMetrics = new ServerMetrics();
val handler = new ConnectionLimitingHandler(Integer.MAX_VALUE, serverMetrics);
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we can create ServerMetrics!!
i think creating ServerMetrics is a better way for the test code!!
i will apply the review thank you!! :)
*/ | ||
public final class ServerMetrics { | ||
|
||
private final LongAdder pendingRequests = new LongAdder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to be used
private final LongAdder pendingRequests = new LongAdder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh right.. i forgot to delete it after some modifications..
i will remove the field!! thank you!!
@@ -823,6 +831,8 @@ static String toString( | |||
buf.append(absoluteUriTransformer); | |||
buf.append(", unhandledExceptionsReportIntervalMillis: "); | |||
buf.append(unhandledExceptionsReportIntervalMillis); | |||
buf.append(", serverMetrics: "); | |||
buf.append(serverMetrics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also implement toString()
for serverMetrics
if we want to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh i will implement toString() method!! thank you!!
@@ -82,6 +85,14 @@ void disconnectWhenFinished() { | |||
} | |||
|
|||
final boolean tryComplete(@Nullable Throwable cause) { | |||
if (req.isHttp1WebSocket()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm concerned decreaseActive*Requests
will never have a chance to be called.
e.g. If a request is received but a connection is immediately closed, will decreaseActive*Requests
always be called?
I'm wondering if the future where the request/response is completed is a more fail-safe location. What do you think @minwoox ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good suggestion. I prefer the location. 👍
Let me resolve the conflict. 😉 |
Motivation:
Add
ServerMetrics
to collect metrics related server. #4992Modifications:
ServerMetrics.increasePendingHttp1Requests()
in
Http1RequestDecoder#channelRead
beforefireChannelRead()
ServerMetrics.increasePendingHttp2Requests()
in
Http2RequestDecoder#onHeadersRead
beforefireChannelRead()
ServerMetrics.decreasePendingHttp1Requests()
,ServerMetrics.decreasePendingHttp2Requests()
,ServerMetrics.increaseActiveHttp1WebSocketRequests()
,ServerMetrics.increaseActiveHttp1Requests()
andServerMetrics.increaseActiveHttp2Requests()
in
HttpServerHandler#serve0
beforeservice.serve(reqCtx, req)
ServerMetrics.decreaseActiveHttp1WebSocketRequests()
,ServerMetrics.decreaseActiveHttp1Requests()
andServerMetrics.decreaseActiveHttp2Requests()
in
AbstractHttpResponseHandler#tryComplete
ServerMetrics.increaseActiveConnectionsAndGet()
andServerMetrics.decreaseActiveConnections()
in
ConnectionLimitingHandler#channelRead
Result: