简介
在之前的文章中,咱们实现了反对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/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!