Skip to content
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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

seongbeenkim
Copy link

@seongbeenkim seongbeenkim commented Apr 22, 2024

Motivation:

Add ServerMetrics to collect metrics related server. #4992

Modifications:

  • Add ServerMetrics.increasePendingHttp1Requests()
    in Http1RequestDecoder#channelRead before fireChannelRead()
  • Add ServerMetrics.increasePendingHttp2Requests()
    in Http2RequestDecoder#onHeadersRead before fireChannelRead()
  • Add ServerMetrics.decreasePendingHttp1Requests(), ServerMetrics.decreasePendingHttp2Requests(), ServerMetrics.increaseActiveHttp1WebSocketRequests(), ServerMetrics.increaseActiveHttp1Requests() and ServerMetrics.increaseActiveHttp2Requests()
    in HttpServerHandler#serve0 before service.serve(reqCtx, req)
  • Add ServerMetrics.decreaseActiveHttp1WebSocketRequests(), ServerMetrics.decreaseActiveHttp1Requests() and ServerMetrics.decreaseActiveHttp2Requests()
    in AbstractHttpResponseHandler#tryComplete
  • Add ServerMetrics.increaseActiveConnectionsAndGet() and ServerMetrics.decreaseActiveConnections()
    in ConnectionLimitingHandler#channelRead

Result:

@CLAassistant
Copy link

CLAassistant commented Apr 22, 2024

CLA assistant check
All committers have signed the CLA.

@minwoox minwoox added this to the 1.29.0 milestone Apr 22, 2024
Copy link
Member

@minwoox minwoox left a 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();
Copy link
Member

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.

Copy link
Author

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);
Copy link
Member

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() {
Copy link
Member

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?

Copy link
Author

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

Comment on lines +467 to +476
if (isHttp1WebSocket) {
serverMetrics.increaseActiveHttp1WebSocketRequests();
serverMetrics.decreasePendingHttp1Requests();
} else if (reqCtx.sessionProtocol().isExplicitHttp1()) {
serverMetrics.increaseActiveHttp1Requests();
serverMetrics.decreasePendingHttp1Requests();
} else if (reqCtx.sessionProtocol().isExplicitHttp2()) {
serverMetrics.increaseActiveHttp2Requests();
serverMetrics.decreasePendingHttp2Requests();
}
Copy link
Member

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 {...}

Copy link
Author

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!

Copy link
Contributor

@jrhee17 jrhee17 left a 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()
Copy link
Contributor

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);
...

Copy link
Author

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();
Copy link
Contributor

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

Suggested change
private final LongAdder pendingRequests = new LongAdder();

Copy link
Author

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);
Copy link
Contributor

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.

Copy link
Author

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()) {
Copy link
Contributor

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 ?

Copy link
Member

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. 👍

@minwoox
Copy link
Member

minwoox commented May 21, 2024

Let me resolve the conflict. 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose the server-side metrics programmatically
4 participants