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

HTTP/1.1 is being logged and recorded by metrics even though HTTP/2 is enabled #3080

Closed
miriyusifli opened this issue Mar 4, 2024 · 10 comments
Assignees
Labels
for/stackoverflow Questions are best asked on SO or Gitter

Comments

@miriyusifli
Copy link

miriyusifli commented Mar 4, 2024

Context

Hi, I have enabled HTTP/2 on the server side and client side. Whenever I send a request using the curl command I am getting the following response as expected. Some configuration-related details:

  • No SSL
  • Server uses Jetty
  • Both H2C and HTTP/1.1 are enabled on the server
HTTP/1.1 101 Switching Protocols

HTTP/2 204
date: Mon, 04 Mar 2024 20:12:20 GMT
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY

Problem

However, every time I send a request from the client application to the server, it records certain HTTP/1.1 logs that I don't understand, despite observing only HTTP/2 requests in Wireshark. Below is the logged HTTP/1.1 related logs.

HTTP/1.1 204 No Content
date: <filtered>
x-content-type-options: <filtered>
x-xss-protection: <filtered>
cache-control: <filtered>
pragma: <filtered>
expires: <filtered>
x-frame-options: <filtered>
content-length: <filtered>
x-http2-stream-id: <filtered>

A response status of 204 is an expected status code according to our business requirements. Additionally, it's worth noting that Netty metrics capture certain HTTP/1.1 calls. Specifically, I'm utilizing the reactor.netty.connection.provider.active.connections metric. However, I'm confused by the presence of two distinct connection providers: one labeled http2.http.client and the other labeled http.client. In the application I only have http.client

image

Here is the full log regarding a call from the client to the server.

2024-03-04 20:09:59.902 DEBUG [] 64006 --- [ctor-http-nio-4] r.n.http.server.HttpServerOperations     : [c075df81, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] Increasing pending responses, now 1
2024-03-04 20:09:59.903 DEBUG [] 64006 --- [ctor-http-nio-4] reactor.netty.http.server.HttpServer     : [c075df81-4, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@1d46b6ad
2024-03-04 20:09:59.908 DEBUG [] 64006 --- [ctor-http-nio-4] reactor.netty.channel.FluxReceive        : [c075df81-4, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] [terminated=false, cancelled=false, pending=0, error=null]: subscribing inbound receiver
2024-03-04 20:09:59.924 DEBUG [] 64006 --- [ctor-http-nio-4] reactor.netty.http.client.Http2Pool      : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] Channel activated
2024-03-04 20:09:59.926 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.Http2Pool      : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] Channel deactivated
2024-03-04 20:09:59.926 DEBUG [] 64006 --- [ds.client-nio-3] r.n.http.client.HttpClientOperations     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090](H2 - -1) New HTTP/2 stream
2024-03-04 20:09:59.926 DEBUG [] 64006 --- [ds.client-nio-3] r.netty.http.client.HttpClientConfig     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090](H2 - -1) Initialized HTTP/2 stream pipeline AbstractHttp2StreamChannel$3{(reactor.left.h2ToHttp11Codec = io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.client.Http2StreamBridgeClientHandler), (reactor.left.httpDecompressor = io.netty.handler.codec.http.HttpContentDecompressor), (reactor.left.httpMetricsHandler = reactor.netty.http.client.HttpClientMetricsHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}
2024-03-04 20:09:59.926 DEBUG [] 64006 --- [ds.client-nio-3] r.netty.http.client.HttpClientConnect    : [0df061c4/2-1, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] Handler is being applied: {uri=http://localhost:9090/events, method=GET}
2024-03-04 20:09:59.927 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.h2             : [id: 0x0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] OUTBOUND HEADERS: streamId=5 headers=DefaultHttp2Headers[:path: /events, :scheme: http, :authority: localhost:9090, :method: GET, user-agent: ReactorNetty/1.0.39, accept: application/octet-stream, accept-encoding: gzip, x-b3-traceid: e6d3e30dace59ac8, x-b3-spanid: 4b42594751ecd447, x-b3-parentspanid: e6d3e30dace59ac8, x-b3-sampled: 1] padding=0 endStream=false
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] WRITE: 9B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 41 01 04 00 00 00 05                      |..A......       |
+--------+-------------------------------------------------+----------------+
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] WRITE: 65B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| c6 86 c5 82 c4 c3 c2 7f 02 10 65 36 64 33 65 33 |..........e6d3e3|
|00000010| 30 64 61 63 65 35 39 61 63 38 7f 02 10 34 62 34 |0dace59ac8...4b4|
|00000020| 32 35 39 34 37 35 31 65 63 64 34 34 37 7f 02 10 |2594751ecd447...|
|00000030| 65 36 64 33 65 33 30 64 61 63 65 35 39 61 63 38 |e6d3e30dace59ac8|
|00000040| c1                                              |.               |
+--------+-------------------------------------------------+----------------+
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] r.n.http.client.Http2ConnectionProvider  : [0df061c4/2-1, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] Stream opened, now: 1 active streams and 255 max active streams.
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] FLUSH
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.h2             : [id: 0x0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] OUTBOUND DATA: streamId=5 padding=0 endStream=true length=0 bytes=
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] WRITE: 9B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 01 00 00 00 05                      |.........       |
+--------+-------------------------------------------------+----------------+
2024-03-04 20:09:59.931 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] WRITE: 0B
2024-03-04 20:09:59.932 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] FLUSH
2024-03-04 20:09:59.994 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] READ: 41B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 20 01 05 00 00 00 05 89 61 96 d0 7a be 94 |.. .......a..z..|
|00000010| 03 4a 68 1d 8a 08 02 69 40 bf 70 0f dc 6d f5 31 |.Jh....i@.p..m.1|
|00000020| 68 df c5 c4 c3 c2 c1 c0 bf                      |h........       |
+--------+-------------------------------------------------+----------------+
2024-03-04 20:09:59.994 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.h2             : [id: 0x0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] INBOUND HEADERS: streamId=5 headers=DefaultHttp2Headers[:status: 204, date: Mon, 04 Mar 2024 19:09:59 GMT, x-content-type-options: nosniff, x-xss-protection: 1; mode=block, cache-control: no-cache, no-store, max-age=0, must-revalidate, pragma: no-cache, expires: 0, x-frame-options: DENY, content-length: 0] padding=0 endStream=true
2024-03-04 20:09:59.995 DEBUG [] 64006 --- [ds.client-nio-3] r.n.http.client.HttpClientOperations     : [0df061c4/2-1, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] Received response (auto-read:false) : FULL_RESPONSE(decodeResult: success, version: HTTP/1.1, content: PooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 256))
HTTP/1.1 204 No Content
date: <filtered>
x-content-type-options: <filtered>
x-xss-protection: <filtered>
cache-control: <filtered>
pragma: <filtered>
expires: <filtered>
x-frame-options: <filtered>
content-length: <filtered>
x-http2-stream-id: <filtered>
2024-03-04 20:09:59.996 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.channel.FluxReceive        : [0df061c4/2-1, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] [terminated=false, cancelled=false, pending=0, error=null]: subscribing inbound receiver
2024-03-04 20:10:00.002 DEBUG [] 64006 --- [ctor-http-nio-4] r.n.http.server.HttpServerOperations     : [c075df81-4, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] Decreasing pending responses, now 0
2024-03-04 20:10:00.002 DEBUG [] 64006 --- [ds.client-nio-3] r.n.http.client.Http2ConnectionProvider  : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090](H2 - 5) Stream closed, now: 0 active streams and 255 max active streams.
2024-03-04 20:10:00.003 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] READ COMPLETE
2024-03-04 20:10:00.003 DEBUG [] 64006 --- [ds.client-nio-3] reactor.netty.http.client.HttpClient     : [0df061c4, L:/127.0.0.1:50750 - R:localhost/127.0.0.1:9090] FLUSH
2024-03-04 20:10:00.004 DEBUG [] 64006 --- [ctor-http-nio-4] r.n.http.server.HttpServerOperations     : [c075df81-4, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] Last HTTP response frame
2024-03-04 20:10:00.004 DEBUG [] 64006 --- [ctor-http-nio-4] reactor.netty.channel.ChannelOperations  : [c075df81-4, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] [HttpServer] Channel inbound receiver cancelled (subscription disposed).
2024-03-04 20:10:00.004 DEBUG [] 64006 --- [ctor-http-nio-4] r.n.http.server.HttpServerOperations     : [c075df81-4, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:60429] Last HTTP packet was sent, terminating the channel

How I configured HTTP/2?

HttpClient httpClient = httpClient(
                HttpClient.create(reactorResourceFactory.getConnectionProvider()), properties, resourceFactory(properties).getLoopResources());


private ReactorResourceFactory resourceFactory(WebClientProperties properties) {
        final ReactorResourceFactory factory = new ReactorResourceFactory();
        factory.setUseGlobalResources(false);
        factory.setConnectionProvider(ConnectionProvider
                .builder(properties.getEventLoopName())
                .maxConnections(properties.getMaxConnection())
                .pendingAcquireMaxCount(properties.getPendingAcquireQueueMaxCount())
                .maxIdleTime(Duration.ofMillis(properties.getMaxIdleTime()))
                .pendingAcquireTimeout(Duration.ofMillis(properties.getAcquireTimeout())).build());
        factory.setLoopResources(LoopResources.create(properties.getEventLoopName(), properties.getWorkers(), properties.isDaemon()));

        return factory;
        
        
  private HttpClient httpClient(HttpClient client, WebClientProperties properties, LoopResources loopResources) {
  return client
                .compress(true)
                .protocol(HttpProtocol.H2C)
                .wiretap(true)
                .metrics(true, Function.identity())
                .runOn(loopResources)
                .option(CONNECT_TIMEOUT_MILLIS, properties.getConnectionTimeout())
                .option(TCP_NODELAY, true)
                .doOnConnected(conn -> {
                    if (properties.getReadTimeout() != null) {
                        conn.addHandlerLast(new ReadTimeoutHandler(properties.getReadTimeout()));
                    }
                    if (properties.getWriteTimeout() != null) {
                        conn.addHandlerLast(new WriteTimeoutHandler(properties.getWriteTimeout()));
                    }
                    if (properties.isEnableLog()) {
                        conn.addHandlerLast(new LoggingHandler(LogLevel.DEBUG));
                    }
                });
    }

Question

Are there any misconfigurations? I'm confused by the appearance of HTTP/1.1 entries in logs and metrics, despite Wireshark exclusively displaying HTTP/2 calls.

@violetagg violetagg added the for/stackoverflow Questions are best asked on SO or Gitter label Mar 5, 2024
@violetagg
Copy link
Member

@miriyusifli

HTTP/1.1 204 No Content
date: <filtered>
x-content-type-options: <filtered>
x-xss-protection: <filtered>
cache-control: <filtered>
pragma: <filtered>
expires: <filtered>
x-frame-options: <filtered>
content-length: <filtered>
x-http2-stream-id: <filtered>

This is an internal implementation detail. At some point Reactor Netty uses for HTTP/2 the same implementation as HTTP/1.1,
because of that you see this log. However you can also see that there definitely a header with stream id is presented, something that you cannot see with HTTP/1.1 request.

I'm confused by the presence of two distinct connection providers: one labeled http2.http.client and the other labeled http.client.

This is also an internal implementation detail. For HTTP/2 we track the connections and the streams.

@violetagg violetagg self-assigned this Mar 5, 2024
@violetagg violetagg added for/user-attention This issue needs user attention (feedback, rework, etc...) and removed ❓need-triage labels Mar 5, 2024
@miriyusifli
Copy link
Author

Thanks for the quick reply @violetagg.

My interest is knowing the number of HTTP connections within the application and its impact on performance. Thus, I am interested in HTTP connections maintained by the application. Should I ignore the recorded HTTP/1.1 (http.client) calls in the metrics and concentrate only on (http2.http.client)?

I would like to know more info about usage of HTTP/1.1 calls recorded by the metric.

@violetagg
Copy link
Member

Thanks for the quick reply @violetagg.

My interest is knowing the number of HTTP connections within the application and its impact on performance. Thus, I am interested in HTTP connections maintained by the application. Should I ignore the recorded HTTP/1.1 (http.client) calls in the metrics and concentrate only on (http2.http.client)?

With http2.http.client you should be able to track:

  • active connections
  • idle connections
  • pending acquire streams
  • active streams

I would like to know more info about usage of HTTP/1.1 calls recorded by the metric.

@miriyusifli
Copy link
Author

miriyusifli commented Mar 5, 2024

The metrics are confusing. E.g:
Screenshot 2024-03-05 at 11 54 03

How is it possible that active connections don't show any HTTP/2 calls even though the application only uses HTTP/2? As you can see Idle connections graph is showing some idle HTTP/2 connections. How a connection can idle without being active?
I have max 20 seconds idle time but according to metrics connections are in the idle state even after 20 seconds.

@violetagg
Copy link
Member

well we make difference between connections and streams: active connection means that we take this connection in order to open a stream, once the stream is opened, the connection is returned to the pool and is idle until we need to open a new stream. So is it possible to add to this graph also the active streams?

@miriyusifli
Copy link
Author

@violetagg
Here it is
Screenshot 2024-03-05 at 13 15 46

Does it use HTTP/1.1 connection to open the stream? Trying to understand why I see HTTP/1.1 connections in graph, but not HTTP/2 connections

@violetagg
Copy link
Member

Yes HTTP/2 pool uses HTTP/1.1 pool for establishing connections to the remote.

@miriyusifli
Copy link
Author

@violetagg

  1. My assumption is that it uses HTTP/1.1 to create streams and then uses streams for communication. Therefore, there is no any HTTP/2 connections recorded on the active connections graph. But interestingly it is showing HTTP/2 connections on the idle connections graph.

  2. Can a stream be used again and again for different requests?

@violetagg
Copy link
Member

@miriyusifli Here is how it is implemented (simplified view):
Request comes to HTTP/2 pool - a connection is established via HTTP/1.1 pool and stays in HTTP/1.1 pool as active, however in HTTP/2 pool this connection will be marked as active only when a stream creation is needed (this should be pretty quick operation) and will stay idle if for the moment there is no need to create streams.
Streams are never reused.

@violetagg violetagg removed the for/user-attention This issue needs user attention (feedback, rework, etc...) label Mar 6, 2024
@violetagg
Copy link
Member

@miriyusifli I'm closing this as IMO Reactor Netty works as expected. For any further questions please ask on Gitter or Stack Overflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for/stackoverflow Questions are best asked on SO or Gitter
Projects
None yet
Development

No branches or pull requests

3 participants