Introduction

In the previous article, we implemented a netty server that supports http2 and successfully accessed it with a browser that supports http2. Although the browser is very versatile, sometimes we also need to use a specific netty client to communicate with the server.

Today we will discuss the netty client's support for http2.

Configure SslContext

Although http2 is not mandatory to support TLS, modern browsers need to enable http2 in the TLS environment, so for the client, it is also necessary to configure the SslContext that supports http2. There is not much difference between the content of the SslContext configuration on the client and the server. The only difference is that you need to call the SslContextBuilder.forClient() instead of the forServer() method to obtain the SslContextBuilder. The code to create the SslContext is as follows:

SslProvider provider =
                    SslProvider.isAlpnSupported(SslProvider.OPENSSL)? SslProvider.OPENSSL : SslProvider.JDK;
            sslCtx = SslContextBuilder.forClient()
                  .sslProvider(provider)
                  .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                  // 因为我们的证书是自生成的,所以需要信任放行
                  .trustManager(InsecureTrustManagerFactory.INSTANCE)
                  .applicationProtocolConfig(new ApplicationProtocolConfig(
                          Protocol.ALPN,
                          SelectorFailureBehavior.NO_ADVERTISE,
                          SelectedListenerFailureBehavior.ACCEPT,
                          ApplicationProtocolNames.HTTP_2,
                          ApplicationProtocolNames.HTTP_1_1))
                  .build();

If SSL is used, the ssl handler must be the first handler in pipline, so the code to add SslContext to pipline is as follows:

ch.pipeline().addFirst(sslCtx.newHandler(ch.alloc()));

Client's handler

Use Http2FrameCodec

By default, netty’s channel can only receive ByteBuf messages. For http2, the underlying transmission is a frame. Direct manipulation of the underlying frame is not particularly friendly to ordinary programmers, so netty provides an Http2FrameCodec to address the underlying The http2 frame is encapsulated into an Http2Frame object to facilitate the processing of the program.

On the server side, we use Http2FrameCodecBuilder.forServer() to create Http2FrameCodec, and on the client side, we use Http2FrameCodecBuilder.forClient() to create Http2FrameCodec:

Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient()
            .initialSettings(Http2Settings.defaultSettings())
            .build();

Then add it to pipline to use:

        ch.pipeline().addLast(http2FrameCodec);

Http2MultiplexHandler and Http2MultiplexCodec

We know that for http2, multiple streams can be created in a TCP connection, and each stream is composed of multiple frames. Taking into account the multiplexing situation, netty can create a separate channel for each stream. For each newly created channel, netty's ChannelInboundHandler can be used to process channel messages, thereby improving netty's processing of http2 s efficiency.

There are two specialized classes in netty for this support for stream creation of new channels, they are Http2MultiplexHandler and Http2MultiplexCodec.

Their functions are the same, Http2MultiplexHandler inherits from Http2ChannelDuplexHandler, it must be used together with Http2FrameCodec. And Http2MultiplexCodec itself inherits from Http2FrameCodec, and has combined the functions of Http2FrameCodec.

public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler

@Deprecated
public class Http2MultiplexCodec extends Http2FrameCodec 

But by checking the source code, we found that Http2MultiplexCodec is an API that is not recommended, so here we mainly introduce Http2MultiplexHandler.

For Http2MultiplexHandler, every time a new stream is created, a new corresponding channel is created, and the application uses this newly created channel to send and receive Http2StreamFrame.

The newly created sub-channel will be registered in Netty’s EventLoop, so for a valid sub-channel, it is not immediately matched to the HTTP/2 stream, but when the first Http2HeadersFrame is successfully sent or received After that, the Event event is triggered, and then the binding operation is performed.

Because it is a child channel, the connection level events, such as Http2SettingsFrame and Http2GoAwayFrame, will be processed by the parent channel first, and then broadcast to the child channel for processing.

At the same time, although Http2GoAwayFrame and Http2ResetFrame indicate that the remote node no longer receives new frames, because the channel itself may also have queue messages, it needs to wait for Channel.read() to be empty before closing.

In addition, for the child channel, because the connection-level flow control window is not known, if there is an overflow message, it will be cached in the buff of the parent channel.

With Http2MultiplexHandler, adding it to the client's pipline allows the client to support multiple channels:

ch.pipeline().addLast(new Http2MultiplexHandler(new SimpleChannelInboundHandler() {
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
                // 处理inbound streams
                log.info("Http2MultiplexHandler接收到消息: {}",msg);
            }
        }))

Use sub-channels to send messages

From the above introduction, we know that once the Http2MultiplexHandler is used, the specific message processing is in the sub-channel. So how can I get the child channel from the parent channel, and then use the child channel to send information?

netty provides the Http2StreamChannelBootstrap class, which provides the open method to create sub-channels:

        final Http2StreamChannel streamChannel;
        try {
            if (ctx.handler() instanceof Http2MultiplexCodec) {
                streamChannel = ((Http2MultiplexCodec) ctx.handler()).newOutboundStream();
            } else {
                streamChannel = ((Http2MultiplexHandler) ctx.handler()).newOutboundStream();
            }

All we have to do is to call this method to create a child channel:

final Http2StreamChannel streamChannel = streamChannelBootstrap.open().syncUninterruptibly().getNow();

Then add a custom Http2ClientStreamFrameHandler that handles Http2StreamFrame to the pipline of the sub-channel:

final Http2ClientStreamFrameHandler streamFrameResponseHandler =
                    new Http2ClientStreamFrameHandler();
streamChannel.pipeline().addLast(streamFrameResponseHandler);

After the preparation is complete, construct an http2 message and send it using streamChannel:

// 发送HTTP2 get请求
            final DefaultHttp2Headers headers = new DefaultHttp2Headers();
            headers.method("GET");
            headers.path(PATH);
            headers.scheme(SSL? "https" : "http");
            Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, true);
            streamChannel.writeAndFlush(headersFrame);

Summarize

The above is the basic operation of using netty's framecode to build http2 client and server to communicate.

For the examples in this article, please refer to: learn-netty4

This article has been included in http://www.flydean.com/32-netty-http2client-framecodec/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "programs, those things", know the technology, know you better!


flydean
890 声望437 粉丝

欢迎访问我的个人网站:www.flydean.com