网易首页 > 网易号 > 正文 申请入驻

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

0
分享至

简介

对于http2协议来说,它的底层跟http1.1是完全不同的,但是为了兼容http1.1协议,http2提供了一个从http1.1升级到http2的方式,这个方式叫做cleartext upgrade,也可以简称为h2c。

在netty中,http2的数据对应的是各种http2Frame对象,而http1的数据对应的是HttpRequest和HttpHeaders。一般来说要想从客户端发送http2消息给支持http2的服务器,那么需要发送这些http2Frame的对象,那么可不可以像http1.1这样发送HttpRequest对象呢?

今天的文章将会给大家揭秘。

使用http1.1的方式处理http2

netty当然考虑到了客户的这种需求,所以提供了两个对应的类,分别是:InboundHttp2ToHttpAdapter和HttpToHttp2ConnectionHandler。

他们是一对方法,其中InboundHttp2ToHttpAdapter将接收到的HTTP/2 frames 转换成为HTTP/1.x objects,而HttpToHttp2ConnectionHandler则是相反的将HTTP/1.x objects转换成为HTTP/2 frames。这样我们在程序中只需要处理http1的对象即可。

他们的底层实际上调用了HttpConversionUtil类中的转换方法,将HTTP2对象和HTTP1对象进行转换。

处理TLS连接

和服务器一样,客户端的连接也需要区分是TLS还是clear text,TLS简单点,只需要处理HTTP2数据即可,clear text复杂点,需要考虑http升级的情况。

先看下TLS的连接处理。

首先是创建SslContext,客户端的创建和服务器端的创建没什么两样,这里要注意的是SslContextBuilder调用的是forClient()方法:

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

然后将sslCtx的newHandler方法传入到pipeline中:

pipeline.addLast(sslCtx.newHandler(ch.alloc(), CustHttp2Client.HOST, CustHttp2Client.PORT));

最后加入ApplicationProtocolNegotiationHandler,用于TLS扩展协议的协商:

pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
ChannelPipeline p = ctx.pipeline();
p.addLast(connectionHandler);
p.addLast(settingsHandler, responseHandler);
return;
ctx.close();
throw new IllegalStateException("未知协议: " + protocol);

如果是HTTP2协议,则需要向pipline中加入三个handler,分别是connectionHandler,settingsHandler和responseHandler。

connectionHandler用于处理客户端和服务器端的连接,这里使用HttpToHttp2ConnectionHandlerBuilder来构建一个上一节提到的HttpToHttp2ConnectionHandler,用来将http1.1对象转换成为http2对象。

Http2Connection connection = new DefaultHttp2Connection(false);
connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
.frameListener(new DelegatingDecompressorFrameListener(
connection,
new InboundHttp2ToHttpAdapterBuilder(connection)
.maxContentLength(maxContentLength)
.propagateSettings(true)
.build()))
.frameLogger(logger)
.connection(connection)
.build();

但是连接其实是双向的,HttpToHttp2ConnectionHandler是将http1.1转换成为http2,它实际上是一个outbound处理器,我们还需要一个inbound处理器,用来将接收到的http2对象转换成为http1.1对象,这里通过添加framelistener来实现。

frameListener传入一个DelegatingDecompressorFrameListener,其内部又传入了前一节介绍的InboundHttp2ToHttpAdapterBuilder用来对http2对象进行转换。

settingsHandler用来处理Http2Settings inbound消息,responseHandler用来处理FullHttpResponse inbound消息。

这两个是自定义的handler类。

处理h2c消息

从上面的代码可以看出,我们在TLS的ProtocolNegotiation中只处理了HTTP2协议,如果是HTTP1协议,直接会报错。如果是HTTP1协议,则可以通过clear text upgrade来实现,也就是h2c协议。

我们看下h2c需要添加的handler:

private void configureClearText(SocketChannel ch) {
HttpClientCodec sourceCodec = new HttpClientCodec();
Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler);
HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 65536);

ch.pipeline().addLast(sourceCodec,
upgradeHandler,
new CustUpgradeRequestHandler(this),
new UserEventLogger());
}

首先添加的是HttpClientCodec作为source编码handler,然后添加HttpClientUpgradeHandler作为upgrade handler。最后添加自定义的CustUpgradeRequestHandler和事件记录器UserEventLogger。

自定义的CustUpgradeRequestHandler负责在channelActive的时候,创建upgradeRequest并发送到channel中。

因为upgradeCodec中已经包含了处理http2连接的connectionHandler,所以还需要手动添加settingsHandler和responseHandler。

ctx.pipeline().addLast(custHttp2ClientInitializer.settingsHandler(), custHttp2ClientInitializer.responseHandler());
发送消息

handler配置好了之后,我们就可以直接以http1的方式来发送http2消息了。

首先发送一个get请求:

// 创建一个get请求
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, GETURL, Unpooled.EMPTY_BUFFER);
request.headers().add(HttpHeaderNames.HOST, hostName);
request.headers().add(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请求
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, POSTURL,
wrappedBuffer(POSTDATA.getBytes(CharsetUtil.UTF_8)));
request.headers().add(HttpHeaderNames.HOST, hostName);
request.headers().add(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());

和普通的http1请求没太大区别。

总结

通过使用InboundHttp2ToHttpAdapter和HttpToHttp2ConnectionHandler可以方便的使用http1的方法来发送http2的消息,非常方便。

本文的例子可以参考:learn-netty4

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

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
短裤 + 运动鞋这样搭,清爽又显高,普通人也能穿出时髦感

短裤 + 运动鞋这样搭,清爽又显高,普通人也能穿出时髦感

巧百搭
2026-05-30 20:16:46
25岁穷小伙表白32岁女房东,对方秒答应,网友:做梦都不敢这么想

25岁穷小伙表白32岁女房东,对方秒答应,网友:做梦都不敢这么想

阿莱美食汇
2026-05-30 01:17:00
当年为什么查办褚时健?

当年为什么查办褚时健?

百晓生谈历史
2025-08-20 21:55:53
整个萨尔布吕肯笼罩在伤感中:樊振东和狂热的中国球迷都要离开了

整个萨尔布吕肯笼罩在伤感中:樊振东和狂热的中国球迷都要离开了

杨华评论
2026-05-30 02:39:28
《主角》直到忆秦娥自首,胡三元才发现,刘红兵自杀的真相

《主角》直到忆秦娥自首,胡三元才发现,刘红兵自杀的真相

娱乐倾城巷
2026-05-30 16:36:13
女子和情夫幽会,完事后丈夫发现有液体,女子找情夫提要求被杀

女子和情夫幽会,完事后丈夫发现有液体,女子找情夫提要求被杀

丫头舫
2026-05-28 11:50:39
伊朗外交部:伊美交流仍在继续,尚未达成最终共识

伊朗外交部:伊美交流仍在继续,尚未达成最终共识

澎湃新闻
2026-05-30 02:32:03
深度长文:真的有床上被征服的女人吗?

深度长文:真的有床上被征服的女人吗?

宇宙时空
2026-05-29 17:10:11
NBA西决G7裁判组:马克-戴维斯担任主裁,他曾吹罚过G3

NBA西决G7裁判组:马克-戴维斯担任主裁,他曾吹罚过G3

懂球帝
2026-05-30 22:37:13
仅200万元!余承东官宣新尊界S800开启预售后,奇怪的现象出现了

仅200万元!余承东官宣新尊界S800开启预售后,奇怪的现象出现了

春雨说科技
2026-05-30 17:45:12
陈凯歌曾评价周迅:如果身高再多上10厘米,那么整个世界就是她的

陈凯歌曾评价周迅:如果身高再多上10厘米,那么整个世界就是她的

寒士之言本尊
2026-05-29 13:04:53
我有一个男朋友, 可他从不碰我,同居了一个月后我发现他有问题

我有一个男朋友, 可他从不碰我,同居了一个月后我发现他有问题

皓皓情感说
2026-05-14 15:11:12
中国女排全新首发揭晓,七仙女全部换人,吴梦洁替身尘埃落定

中国女排全新首发揭晓,七仙女全部换人,吴梦洁替身尘埃落定

酷侃体坛
2026-05-30 12:10:44
偷偷结婚生子?移民国外?李梓萌消失2月引争议,担心的事发生了

偷偷结婚生子?移民国外?李梓萌消失2月引争议,担心的事发生了

离离言几许
2026-03-16 16:31:23
路撑不住,养护成本暴增40%!电车太重让全民买单,车企该改变了

路撑不住,养护成本暴增40%!电车太重让全民买单,车企该改变了

王新喜
2026-05-28 17:08:11
讨论对华新限制措施,内部多国持谨慎态度,欧盟这次会议暴露深层次焦虑

讨论对华新限制措施,内部多国持谨慎态度,欧盟这次会议暴露深层次焦虑

环球网资讯
2026-05-30 06:40:11
你见过的土豪是什么样子的?网友:前台小姐直接跟着他走了

你见过的土豪是什么样子的?网友:前台小姐直接跟着他走了

特约前排观众
2025-08-19 00:20:03
自由市场大鱼评级:米神C级,威少B级,哈登A级,他S级抢着要

自由市场大鱼评级:米神C级,威少B级,哈登A级,他S级抢着要

林子说事
2026-05-30 20:11:04
187票!高市这次不是赢,是通杀!日本从此高市一人独掌乾坤?

187票!高市这次不是赢,是通杀!日本从此高市一人独掌乾坤?

霁寒飘雪
2026-05-30 12:03:26
白云机场终于回应!三大痛点:接驳不便、滑行过长、地毯拖行李费力

白云机场终于回应!三大痛点:接驳不便、滑行过长、地毯拖行李费力

童童聊娱乐啊
2026-05-30 15:46:53
2026-05-31 06:35:00
flydean程序那些事
flydean程序那些事
最通俗的解读,最深刻的干货!
356文章数 438关注度
往期回顾 全部

科技要闻

车圈大佬发声:价格战远去,但竞争仍残酷

头条要闻

两名9岁女孩被困电梯近2小时 求救几十次物业无动于衷

头条要闻

两名9岁女孩被困电梯近2小时 求救几十次物业无动于衷

体育要闻

巴黎再度捧起欧冠奖杯 枪手众将黯然神伤

娱乐要闻

张碧晨《歌手》 “活人微死” 自嘲

财经要闻

双汇管不住一头猪

汽车要闻

900V+3.2秒破百 领克10+&领克10上市16.99万元起

态度原创

游戏
本地
时尚
数码
公开课

巫师3新DLC跨十年:年轻玩家在初发售时还是小孩!

本地新闻

用剪纸的方式,打开江苏扬州

美回巅峰的她们,带火的这些爆款真的好用吗

数码要闻

vivo S60系列发布:2899元起 推出4K原生感Live

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版