From b7ab9e0a22f62647c8745f106d620ee307e7a2e7 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 22 Dec 2015 21:09:57 +0100 Subject: [PATCH] 484818 - Expose interesting HTTP/2 attributes and operations via JMX. Initial work to expose already existing attributes on the server. --- .../http2/client/HTTP2ClientSession.java | 3 +- jetty-http2/http2-common/pom.xml | 44 +++++++++++-------- .../http2/AbstractFlowControlStrategy.java | 25 ++++++++++- .../http2/BufferingFlowControlStrategy.java | 9 ++++ .../org/eclipse/jetty/http2/HTTP2Session.java | 28 ++++++++++-- .../AbstractHTTP2ServerConnectionFactory.java | 29 +++++++++++- .../server/AbstractConnectionFactory.java | 29 +++++++----- 7 files changed, 128 insertions(+), 39 deletions(-) diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java index 3822d25779c..8f80eefcc24 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.http2.client; import org.eclipse.jetty.http2.FlowControlStrategy; import org.eclipse.jetty.http2.HTTP2Session; import org.eclipse.jetty.http2.IStream; +import org.eclipse.jetty.http2.api.Session; import org.eclipse.jetty.http2.api.Stream; import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.frames.PushPromiseFrame; @@ -35,7 +36,7 @@ public class HTTP2ClientSession extends HTTP2Session { private static final Logger LOG = Log.getLogger(HTTP2ClientSession.class); - public HTTP2ClientSession(Scheduler scheduler, EndPoint endPoint, Generator generator, Listener listener, FlowControlStrategy flowControl) + public HTTP2ClientSession(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl) { super(scheduler, endPoint, generator, listener, flowControl, 1); } diff --git a/jetty-http2/http2-common/pom.xml b/jetty-http2/http2-common/pom.xml index be2213dc8f4..f4359f4a0a5 100644 --- a/jetty-http2/http2-common/pom.xml +++ b/jetty-http2/http2-common/pom.xml @@ -16,6 +16,12 @@ http2-hpack ${project.version} + + org.eclipse.jetty + jetty-jmx + ${project.version} + true + org.eclipse.jetty.toolchain jetty-test-helper @@ -23,30 +29,30 @@ - - ${project.groupId}.common - + + ${project.groupId}.common + - org.apache.felix - maven-bundle-plugin - true - - - - manifest - - - - org.eclipse.jetty.http2.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"; - - - - + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + org.eclipse.jetty.http2.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"; + + + + - + diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java index 318ed812372..e6ebc250eb1 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java @@ -20,9 +20,12 @@ package org.eclipse.jetty.http2; import org.eclipse.jetty.http2.api.Stream; import org.eclipse.jetty.http2.frames.WindowUpdateFrame; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +@ManagedObject public abstract class AbstractFlowControlStrategy implements FlowControlStrategy { protected static final Logger LOG = Log.getLogger(FlowControlStrategy.class); @@ -36,12 +39,14 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy this.initialStreamRecvWindow = DEFAULT_WINDOW_SIZE; } - protected int getInitialStreamSendWindow() + @ManagedAttribute(value = "The initial size of stream's flow control send window", readonly = true) + public int getInitialStreamSendWindow() { return initialStreamSendWindow; } - protected int getInitialStreamRecvWindow() + @ManagedAttribute(value = "The initial size of stream's flow control receive window", readonly = true) + public int getInitialStreamRecvWindow() { return initialStreamRecvWindow; } @@ -102,6 +107,8 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy int oldSize = stream.updateSendWindow(delta); if (LOG.isDebugEnabled()) LOG.debug("Updated stream send window {} -> {} for {}", oldSize, oldSize + delta, stream); + if (oldSize <= 0) + onStreamUnstalled(stream); } } else @@ -109,6 +116,8 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy int oldSize = session.updateSendWindow(delta); if (LOG.isDebugEnabled()) LOG.debug("Updated session send window {} -> {} for {}", oldSize, oldSize + delta, session); + if (oldSize <= 0) + onSessionUnstalled(session); } } @@ -166,4 +175,16 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy if (LOG.isDebugEnabled()) LOG.debug("Stream stalled {}", stream); } + + protected void onSessionUnstalled(ISession session) + { + if (LOG.isDebugEnabled()) + LOG.debug("Session unstalled {}", session); + } + + protected void onStreamUnstalled(IStream stream) + { + if (LOG.isDebugEnabled()) + LOG.debug("Stream unstalled {}", stream); + } } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java index 819e7703e45..875d472da0a 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java @@ -26,6 +26,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.util.Atomics; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; /** *

A flow control strategy that accumulates updates and emits window control @@ -49,6 +51,7 @@ import org.eclipse.jetty.util.Callback; *

The application consumes the remaining 15, so now SB=15, and no window * control frame is emitted.

*/ +@ManagedObject public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy { private final AtomicInteger maxSessionRecvWindow = new AtomicInteger(DEFAULT_WINDOW_SIZE); @@ -67,6 +70,12 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy this.bufferRatio = bufferRatio; } + @ManagedAttribute("The ratio between the receive buffer and the consume buffer") + public float getBufferRatio() + { + return bufferRatio; + } + @Override public void onStreamCreated(IStream stream) { diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java index b57882e9777..2d91e1d85d8 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java @@ -54,11 +54,15 @@ import org.eclipse.jetty.util.Atomics; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.CountingCallback; import org.eclipse.jetty.util.Promise; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Scheduler; -public abstract class HTTP2Session implements ISession, Parser.Listener +@ManagedObject +public abstract class HTTP2Session extends ContainerLifeCycle implements ISession, Parser.Listener { private static final Logger LOG = Log.getLogger(HTTP2Session.class); @@ -73,7 +77,7 @@ public abstract class HTTP2Session implements ISession, Parser.Listener private final Scheduler scheduler; private final EndPoint endPoint; private final Generator generator; - private final Listener listener; + private final Session.Listener listener; private final FlowControlStrategy flowControl; private final HTTP2Flusher flusher; private int maxLocalStreams; @@ -81,7 +85,7 @@ public abstract class HTTP2Session implements ISession, Parser.Listener private long streamIdleTimeout; private boolean pushEnabled; - public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Listener listener, FlowControlStrategy flowControl, int initialStreamId) + public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId) { this.scheduler = scheduler; this.endPoint = endPoint; @@ -98,6 +102,14 @@ public abstract class HTTP2Session implements ISession, Parser.Listener this.pushEnabled = true; // SPEC: by default, push is enabled. } + @Override + protected void doStart() throws Exception + { + addBean(flowControl); + super.doStart(); + } + + @ManagedAttribute(value = "The flow control strategy", readonly = true) public FlowControlStrategy getFlowControlStrategy() { return flowControl; @@ -123,6 +135,7 @@ public abstract class HTTP2Session implements ISession, Parser.Listener this.maxRemoteStreams = maxRemoteStreams; } + @ManagedAttribute("The stream's idle timeout") public long getStreamIdleTimeout() { return streamIdleTimeout; @@ -709,17 +722,25 @@ public abstract class HTTP2Session implements ISession, Parser.Listener return result; } + @ManagedAttribute("The number of active streams") + public int getStreamCount() + { + return streams.size(); + } + @Override public IStream getStream(int streamId) { return streams.get(streamId); } + @ManagedAttribute(value = "The flow control send window", readonly = true) public int getSendWindow() { return sendWindow.get(); } + @ManagedAttribute(value = "The flow control receive window", readonly = true) public int getRecvWindow() { return recvWindow.get(); @@ -753,6 +774,7 @@ public abstract class HTTP2Session implements ISession, Parser.Listener } @Override + @ManagedAttribute(value = "Whether HTTP/2 push is enabled", readonly = true) public boolean isPushEnabled() { return pushEnabled; diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index f625aaa2329..61f581c2016 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -31,15 +31,21 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.LifeCycle; +@ManagedObject public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConnectionFactory { + private final Connection.Listener connectionListener = new ConnectionListener(); + private final HttpConfiguration httpConfiguration; private int maxDynamicTableSize = 4096; private int initialStreamSendWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE; private int maxConcurrentStreams = -1; private int maxHeaderBlockFragment = 0; - private final HttpConfiguration httpConfiguration; public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration) { @@ -50,8 +56,10 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne { super(protocols); this.httpConfiguration = Objects.requireNonNull(httpConfiguration); + addBean(httpConfiguration); } + @ManagedAttribute("The HPACK dynamic table maximum size") public int getMaxDynamicTableSize() { return maxDynamicTableSize; @@ -62,6 +70,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne this.maxDynamicTableSize = maxDynamicTableSize; } + @ManagedAttribute("The initial size of stream's flow control send window") public int getInitialStreamSendWindow() { return initialStreamSendWindow; @@ -72,6 +81,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne this.initialStreamSendWindow = initialStreamSendWindow; } + @ManagedAttribute("The max number of concurrent streams per session") public int getMaxConcurrentStreams() { return maxConcurrentStreams; @@ -116,7 +126,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne ServerParser parser = newServerParser(connector, session); HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); - + connection.addListener(connectionListener); return configure(connection, connector, endPoint); } @@ -131,4 +141,19 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne { return new ServerParser(connector.getByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize()); } + + private static class ConnectionListener extends ContainerLifeCycle implements Connection.Listener + { + @Override + public void onOpened(Connection connection) + { + addManaged((LifeCycle)((HTTP2Connection)connection).getSession()); + } + + @Override + public void onClosed(Connection connection) + { + removeBean(((HTTP2Connection)connection).getSession()); + } + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java index 04623faec0f..146e2085954 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java @@ -26,22 +26,25 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.ArrayUtil; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; - -/* ------------------------------------------------------------ */ /** - * Abstract ConnectionFactory - *

Provides the common handling for {@link ConnectionFactory} implementations including: