当前位置: 首页 > 后端技术 > Java

netty系列:使用http1.1搭建客户端连接http2服务器

时间:2023-04-01 21:06:50 Java

简介对于http2协议,其底层与http1.1完全不同,但是为了兼容http1.1协议,http2提供了一种从http1.1升级到http2的方式,叫做明文升级,也可以简称为h2c。在netty中http2数据对应各种http2Frame对象,而http1数据对应HttpRequest和HttpHeaders。一般来说,如果要从客户端向支持http2的服务器发送http2消息,就需要发送这些http2Frame对象,那么能不能像http1.1一样发送HttpRequest对象呢?今天的文章就为您揭晓其中的秘密。使用http1.1来处理http2netty当然是考虑到了客户的需求,所以提供了两个对应的类,分别是:InboundHttp2ToHttpAdapter和HttpToHttp2ConnectionHandler。它们是一对方法,其中InboundHttp2ToHttpAdapter将接收到的HTTP/2帧转换为HTTP/1.x对象,而HttpToHttp2ConnectionHandler将HTTP/1.x对象反向转换为HTTP/2帧。这样,我们在程序中只需要处理http1这个对象即可。它们的底层其实是调用了HttpConversionUtil类中的转换方法来实现HTTP2对象和HTTP1对象的转换。处理TLS连接和服务端一样,客户端连接也需要区分是TLS还是明文。TLS更简单,只需要处理HTTP2数据。明文比较复杂,需要考虑http升级的情况。先看TLS的连接处理。首先是创建SslContext。客户端的创建与服务器的创建没有区别。这里需要注意的是SslContextBuilder调用了forClient()方法:SslProviderprovider=SslProvider.isAlpnSupported(SslProvider.OPENSSL)?SslProvider.OPENSSL:SslProvider.JDK;sslCtx=SslContextBuilder.forClient().sslProvider(provider).ciphers(Http2SecurityUtil.CIPHERS,SupportedCipherSuiteFilter.INSTANCE)//因为我们的证书是自己生成的,所以需要信任release.trustManager(InsecureTrustManagerFactory.INSTANCE).applicationProtocolConfig(newApplicationProtocol.ALPN,SelectorFailureBehavior.NO_ADVERTISE,SelectedListenerFailureBehavior.ACCEPT,ApplicationProtocolNames.HTTP_2,ApplicationProtocolNames.HTTP_1_1)).build();然后将sslCtx的newHandler方法传入管道:pipeline.addLast(sslCtx.newHandler(ch.alloc(),CustHttp2Client.HOST,CustHttp2Client.PORT));最后,添加ApplicationProtocolNegotiationHandler用于TLS扩展协议的协商:ctx.pipeline();p.addLast(connectionHandler);p.addLast(settingsHandler,responseHandler);返回;}ctx.close();thrownewIllegalStateException("Unknownprotocol:"+protocol);}});如果是HTTP2协议,需要在pipline中添加三个handler,分别是connectionHandler、settingsHandler和responseHandlerconnectionHandler,用于处理client和server端的连接,这里使用HttpToHttp2ConnectionHandlerBuilder构建一个上节提到的HttpToHttp2ConnectionHandler,用于将http1.1对象转换为http2对象。Http2Connection连接=新的DefaultHttp2Connection(false);connectionHandler=newHttpToHttp2ConnectionHandlerBuilder().frameListener(newDelegatingDecompressorFrameListener(connection,newInboundHttp2ToHttpAdapterBuilder(connection).maxContentLength(maxContentLength).propagateSettings(true).build())).frameLogger(logger)。连接(连接).build();但是连接其实是双向的,HttpToHttp2ConnectionHandler把http1.1转换成http2,它其实是一个outbound处理器,我们还需要一个inbound处理器,把接收到的http2对象转换成http1.1对象,这是通过添加一个来实现的框架侦听器在这里。frameListener传入一个DelegatingDecompressorFrameListener,同时传入上一节介绍的InboundHttp2ToHttpAdapterBuilder,用于转换http2对象。settingsHandler用于处理Http2Settings入站消息,responseHandler用于处理FullHttpResponse入站消息。这两个是自定义处理程序类。处理h2c消息从上面的代码可以看出,我们在TLS的ProtocolNegotiation中只处理了HTTP2协议。如果是HTTP1协议,会直接报错。如果是HTTP1协议,可以通过明文升级实现,即h2c协议。我们看下h2c需要添加的handler:privatevoidconfigureClearText(SocketChannelch){Http2ClientUpgradeCodecupgradeCodec=newHttp2ClientUpgradeCodec(connectionHandler);HttpClientUpgradeHandlerupgradeHandler=newHttpClientUpgradeHandler(sourceCodec,upgradeCodec,65536);ch.pipeline().addLast(sourceCodec,upgradeHandler,newCustUpgradeRequestHandler(this),newUserEventLogger());首先添加HttpClientCodec作为源编码处理程序,然后添加HttpClientUpgradeHandler作为升级处理程序。最后添加自定义CustUpgradeRequestHandler和事件记录器UserEventLogger。自定义CustUpgradeRequestHandler负责创建升级请求并在channelActive时将其发送到通道。因为upgradeCodec已经包含了处理http2连接的connectionHandler,所以还需要手动添加settingsHandler和responseHandler。ctx.pipeline().addLast(custHttp2ClientInitializer.settingsHandler(),custHttp2ClientInitializer.responseHandler());配置好发送消息处理器后,我们就可以直接以http1的形式发送http2消息了。首先发送一个get请求://创建一个get请求FullHttpRequestrequest=newDefaultFullHttpRequest(HTTP_1_1,GET,GETURL,Unpooled.EMPTY_BUFFER);request.headers().add(HttpHeaderNames.HOST,hostName);请求头()。添加(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(),scheme.name());request.headers().add(HttpHeaderNames.ACCEPT_ENCODING,HttpHeaderValues.GZIP);request.headers().add(HttpHeaderNames.ACCEPT_ENCODING,HttpHeaderValues.DEFLATE);responseHandler.put(streamId,channel.write(request),channel.newPromise());然后是一个post请求://创建一个post请求request.headers().add(HttpHeaderNames.HOST,hostName);request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(),sc血红素名称());request.headers().add(HttpHeaderNames.ACCEPT_ENCODING,HttpHeaderValues.GZIP);request.headers().add(HttpHeaderNames.ACCEPT_ENCODING,HttpHeaderValues.DEFLATE);responseHandler.put(streamId,channel.write(request),channel.newPromise());和普通的http1请求没有太大区别。总结通过使用InboundHttp2ToHttpAdapter和HttpToHttp2ConnectionHandler,可以很方便的使用http1方法发送http2消息,非常方便。本文示例可参考:learn-netty4本文已收录于http://www.flydean.com/30-netty-http2client-md/最通俗的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!

最新推荐
猜你喜欢