简介

在之前的文章中,咱们实现了反对http2的netty服务器,并且应用反对http2的浏览器胜利的进行拜访。尽管浏览器十分通用,然而有时候咱们也须要应用特定的netty客户端去和服务器进行通信。

明天咱们来探讨一下netty客户端对http2的反对。

配置SslContext

尽管http2并不强制要求反对TLS,然而古代浏览器都是须要在TLS的环境中开启http2,所以对于客户端来说,同样须要配置好反对http2的SslContext。客户端和服务器端配置SslContext的内容没有太大的区别,惟一的区别就是须要调用SslContextBuilder.forClient()而不是forServer()办法来获取SslContextBuilder,创立SslContext的代码如下:

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

如果应用SSL,那么ssl handler必须是pipline中的第一个handler,所以将SslContext退出到pipline中的代码如下:

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

客户端的handler

应用Http2FrameCodec

netty的channel默认只能接管ByteBuf音讯,对于http2来说,底层传输的是一个个的frame,间接操作底层的frame对于一般程序员来说并不是特地敌对,所以netty提供了一个Http2FrameCodec来对底层的http2 frame进行封装成Http2Frame对象,不便程序的解决。

在服务器端咱们应用Http2FrameCodecBuilder.forServer()来创立Http2FrameCodec,在客户端咱们应用Http2FrameCodecBuilder.forClient()来创立Http2FrameCodec:

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

而后将其退出到pipline中即可应用:

        ch.pipeline().addLast(http2FrameCodec);

Http2MultiplexHandler和Http2MultiplexCodec

咱们晓得对于http2来说一个TCP连贯中能够创立多个stream,每个stream又是由多个frame来组成的。思考到多路复用的状况,netty能够为每一个stream创立一个独自的channel,对于新创建的每个channel来说,都能够应用netty的ChannelInboundHandler来对channel的音讯进行解决,从而晋升netty解决http2的效率。

而这个对stream创立新channel的反对,在netty中有两个专门的类,他们是Http2MultiplexHandler和Http2MultiplexCodec。

他们的性能是一样的,Http2MultiplexHandler继承自Http2ChannelDuplexHandler,它必须和 Http2FrameCodec一起应用。而Http2MultiplexCodec自身就是继承自Http2FrameCodec,曾经联合了Http2FrameCodec的性能。

public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler@Deprecatedpublic class Http2MultiplexCodec extends Http2FrameCodec 

然而通过查看源代码,咱们发现Http2MultiplexCodec是不举荐应用的API,所以这里咱们次要介绍Http2MultiplexHandler。

对于Http2MultiplexHandler来说,每次新创建一个stream,都会创立一个新的对应的channel,应用程序应用这个新创建的channel来发送和接管Http2StreamFrame。

新创建的子channel会被注册到netty的EventLoop中,所以对于一个无效的子channel来说,并不是立即就会被匹配到HTTP/2 stream下来,而是当第一个Http2HeadersFrame胜利被发送或者接管之后,才会触发Event事件,进而进行绑定操作。

因为是子channel,所以对于connection level的事件,比方Http2SettingsFrame 和 Http2GoAwayFrame会首先被父channel进行解决,而后再播送到子channel中进行解决。

同时,尽管Http2GoAwayFrame 和 Http2ResetFrame示意近程节点曾经不再接管新的frame了,然而因为channel自身还可能有queue的音讯,所以须要期待Channel.read()为空之后,才会进行敞开操作。

另外对于子channel来说,因为不能晓得connection-level流控制window,所以如果有溢出的音讯会被缓存在父channel的buff中。

有了Http2MultiplexHandler,将其退出client的pipline就能够让客户端反对多路的channel了:

ch.pipeline().addLast(new Http2MultiplexHandler(new SimpleChannelInboundHandler() {            @Override            protected void channelRead0(ChannelHandlerContext ctx, Object msg) {                // 解决inbound streams                log.info("Http2MultiplexHandler接管到音讯: {}",msg);            }        }))

应用子channel发送音讯

从下面的介绍咱们晓得,一旦应用了Http2MultiplexHandler,那么具体的音讯解决就是在子channel中了。那么怎么能力从父channel中获取子channel,而后应用子channel来发送信息呢?

netty提供Http2StreamChannelBootstrap类,它提供了open办法,来创立子channel:

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

咱们要做的就是调用这个办法,来创立子channel:

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

而后将自定义的,专门解决Http2StreamFrame的Http2ClientStreamFrameHandler,增加到子channel的pipline中即可:

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

筹备结束,构建http2音讯,应用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);

总结

以上就是应用netty的framecode构建http2的客户端和服务器端进行通信的基本操作了。

本文的例子能够参考:learn-netty4

本文已收录于 http://www.flydean.com/32-netty-http2client-framecodec/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!