介绍在上一篇文章中,我们实现了一个支持http2的netty服务端,使用支持http2的浏览器成功访问。浏览器虽然很通用,但有时候我们也需要使用特定的netty客户端与服务端进行通信。今天我们就来讨论下netty客户端对http2的支持。配置SslContext虽然http2不需要支持TLS,但是现代浏览器需要在TLS环境下开启http2,所以对于客户端来说,也需要配置支持http2的SslContext。客户端和服务端配置SslContext的内容没有太大区别,唯一的区别是需要调用SslContextBuilder.forClient()而不是forServer()方法来获取SslContextBuilder,创建SslContext的代码如下:SslProviderprovider=SslProvider.isAlpnSupported(SslProvider.OPENSSL)?SslProvider.OPENSSL:SslProvider.JDK;sslCtx=SslContextBuilder.forClient().sslProvider(provider).ciphers(Http2SecurityUtil.CIPHERS,SupportedCipherSuiteFilter.INSTANCE)//因为我们的证书是自己生成的,所以需要信任发布.trustManager(InsecureTrustManagerFactory.INSTANCE).applicationProtocolConfig(newApplicationProtocolConfig(Protocol.ALPN,SelectorFailureBehavior.NO_ADVERTISE,SelectedListenerFailureBehavior.ACCEPT,ApplicationProtocolNames.HTTP_2,ApplicationProtocolNames.HTTP_1_1)).build();如果使用SSL,那么sslhandler必须是管道中的第一个处理程序,因此将SslContext添加到管道的代码如下:ch.pipeline().addFirst(sslCtx.newHandler(ch.alloc()));client的handler使用Http2FrameCodecnetty这个通道,默认只接收ByteBuf消息,对于http2来说,底层是逐帧传输的,直接操作底层frame对普通程序员来说不是特别友好,所以netty提供了一个Http2FrameCodec来封装http2底层frame变成一个Http2Frame对象,方便程序的处理在服务端我们使用Http2FrameCodecBuilder.forServer()创建Http2FrameCodec,在客户端我们使用Http2FrameCodecBuilder.forClient()创建Http2FrameCodec:Http2FrameCodechttp2FrameCodec=Http2FrameCodecBuilder.forClient().initialSettings(Http2FrameCodecBuilder.forClient().initialSettings.build();然后添加到pipline中使用:ch.pipeline().addLast(http2FrameCodec);Http2MultiplexHandler和Http2MultiplexCodec我们知道对于http2来说,可以创建多个流在一个TCP连接中,每个流可以由多个帧组成,考虑到多路复用的情况,netty可以为每个流创建一个单独的通道。对于每一个新创建的通道,都可以使用netty的ChannelInboundHandler来处理通道的消息,从而提高netty处理http2的效率。而这种支持stream创建新通道的功能在netty中有两个特殊的类,它们是Http2MultiplexHandler和Http2MultiplexCodec。它们的功能是一样的,Http2MultiplexHandler继承自Http2ChannelDuplexHandler,必须和Http2FrameCodec一起使用。Http2MultiplexCodec本身继承自Http2FrameCodec,结合了Http2FrameCodec的功能。publicfinalclassHttp2MultiplexHandlerextendsHttp2ChannelDuplexHandler@DeprecatedpublicclassHttp2MultiplexCodecextendsHttp2FrameCodec但是通过查看源码发现Http2MultiplexCodec是一个废弃的API,所以这里主要介绍Http2MultiplexHandler。对于Http2MultiplexHandler,每次创建一个新的流,都会创建一个新的对应通道,应用程序使用这个新创建的通道来发送和接收Http2StreamFrame。新创建的子通道会注册到netty的EventLoop中,所以对于一个有效的子通道,它不会立即匹配到HTTP/2流,而是当第一个Http2HeadersFrame被成功发送或接收后,Event事件将被触发,然后执行绑定操作。因为是子通道,对于连接级别的事件,比如Http2SettingsFrame、Http2GoAwayFrame,会先由父通道处理,然后广播到子通道处理。同时,虽然Http2GoAwayFrame和Http2ResetFrame表示远端节点不再接收新的帧,但由于通道本身可能还有队列消息,需要等待Channel.read()为空后再关闭。另外,对于子通道,由于无法知道连接级的流控窗口,如果有溢出消息,会缓存在父通道的buff中。使用Http2MultiplexHandler,将其添加到客户端的管道允许客户端支持多通道通道:ch.pipeline().addLast(newHttp2MultiplexHandler(newSimpleChannelInboundHandler()){@OverrideprotectedvoidchannelRead0(ChannelHandlerContextctx,Objectmsg){//Handleinboundstreamslog.info("Http2MultiplexHandlerreceivedmessage:{}",msg);}}))使用子通道发送消息从上面的介绍我们知道一旦使用了Http2MultiplexHandler,具体的消息处理在子频道。那么我们如何才能从父频道获取子频道,然后使用子频道发送信息呢?netty提供了Http2StreamChannelBootstrap类,该类提供了创建子通道的open方法:finalHttp2StreamChannelstreamChannel;try{if(ctx.handler()instanceofHttp2MultiplexCodec){streamChannel=((Http2MultiplexCodec)ctx.handler)new()).}else{streamChannel=((Http2MultiplexHandler)ctx.handler()).newOutboundStream();我们所要做的就是调用这个方法来创建一个子通道:然后将专门处理Http2StreamFrame的自定义Http2ClientStreamFrameHandler添加到子通道的pipeline中:streamChannel.pipelineRes()。消息,使用streamChannel发送://SendHTTP2getrequestfinalDefaultHttp2Headersheaders=newDefaultHttp2Headers();headers.method("GET");标题.路径(路径);headers.scheme(SSL?“https”:“http”);Http2HeadersFrameheadersFrame=newDefaultHttp2HeadersFrame(headers,true);streamChannel.writeAndFlush(headersFrame);以上就是使用netty的framecode搭建http2客户端和服务端进行通信的基本操作。本文示例可参考:learn-netty4本文已收录于http://www.flydean.com/32-netty-http2client-framecodec/最通俗解读,最深刻干货,最简洁教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!
