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 port tag to server metrics #6116

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

Conversation

minwoox
Copy link
Contributor

@minwoox minwoox commented Feb 20, 2025

Motivation:
A server can be configured with multiple ports, and operators may want to collect connection and request metrics per port for better observability and debugging.

Modifications:

  • Added a port tag to server metrics.
  • If a DomainSocketAddress is used, the path of the address is used as the tag value.

Result:

Motivation:
A server can be configured with multiple ports, and operators may want to collect connection and request metrics per port
for better observability and debugging.

Modifications:
- Added a `port` tag to server metrics.
- If a `DomainSocketAddress` is used, the path of the address is used as the tag value.

Result:
- Enabled per-port metrics collection for better monitoring and troubleshooting.
@minwoox minwoox added this to the 1.32.0 milestone Feb 20, 2025
Comment on lines 55 to 64
private final Map<Integer, LongAdder> pendingHttp1Requests;
private final Map<Integer, LongAdder> pendingHttp2Requests;
private final Map<Integer, LongAdder> activeHttp1WebSocketRequests;
private final Map<Integer, LongAdder> activeHttp1Requests;
private final Map<Integer, LongAdder> activeHttp2Requests;
private final Map<String, LongAdder> domainSocketPendingHttp1Requests;
private final Map<String, LongAdder> domainSocketPendingHttp2Requests;
private final Map<String, LongAdder> domainSocketActiveHttp1WebSocketRequests;
private final Map<String, LongAdder> domainSocketActiveHttp1Requests;
private final Map<String, LongAdder> domainSocketActiveHttp2Requests;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional) The implementation that holds many Maps in this class looks complex. How about introducing ServerMetricsGroup to encapsulate the complexity?
My suggestion:

public interface ServerMetrics extends MeterBinder {
}

class ServerPortMetrics implements ServerMetrics {
    ...
}

class ServerMetricsGroup implements ServerMetrics {

    private Map<Object, ServerPortMetrics> delegates;
    ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand your intention.
So the ServerMetricsGroup has all the delegates?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I imagined the following implementation:

class ServerMetricsGroup implements ServerMetrics {

  private Map<Object, ServerPortMetrics> delegates;

  void increasePendingHttp1Requests(SocketAddress socketAddress) {
      Object key = getPathOrPort(socketAddress);
      delegates.get(key).increasePendingHttp1Requests();
  }

  public long pendingHttp1Requests() {
      // Sum all pendingHttp1Requests in delegates and return
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks much simpler. 👍
Let me use that approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my second thought, I've realized that I didn't want to use the Object as a key because

  • InetSocketAddress is used mostly for applications
  • So the integer port needs to box and unbox.

Let me develop your idea and avoid those unboxing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split to InetSocketServerMetrics and DomainSocketServerMetrics. PTAL. 🙇‍♂️

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! 👍

@minwoox
Copy link
Contributor Author

minwoox commented Feb 21, 2025

@yzfeng2020 Please let me know if the current implementation if fine. 😉

} finally {
lock.unlock();
}

config().serverMetrics().addActivePort(actualPort);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note) It slightly bothers me that the active port is registered after binding to the port (since technically a connection could be attempted immediately and may not be able to find a LongAdder).

In practice, I assume this won't happen much though.

Alternatively, another way that could've been possible seems to be using channel attributes.

PerPortMetrics perPortMetrics;
...
// use the metrics from the attribute to record metrics
CompletionStage<Void> doStart(@Nullable Void arg) {
  Bootstrap b;
  b.childAttr(AttributeKey.valueOf("SERVER_METRICS"), serverPortMetrics);
}
...
void operationComplete(ChannelFuture f) {
  if (f.isSuccess()) {
    // bind each port separately
    serverPortMetrics.bind(meterRegistry, Tags.of(""));
...
}

I'm fine with the current implementation though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a brilliant idea. 👍
Changed to use the attribute and reverted the ServerMetrics to a concrete class since it's unlikely that a user would need to implement her own ServerMetrics.

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.

ServerMetrics does not support multiple ports properly
3 participants