netty系列之:搭建客户端使用http1.1的方式连接http2服务器
简介
对于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/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
netty系列之:搭建客户端使用http1.1的方式连接http2服务器的更多相关文章
- Centos6架设GIT服务,windows客户端使用TortoiseGit加载KEYGEN连接GIT服务器
前几天得空,想起前一阵学了GIT还没好好实践,就在虚拟机中安装测试了一下,并简单记录了CENTOS6中GIT安装,ssh-keygen生成,客户端使用TortoiseGit加载KEYGEN连接GIT服 ...
- centos7搭建SVN并配置使用http方式访问SVN服务器
一.检查SVN是否安装 centos7系统自带SVN # rpm -qa subversion [root@localhost ~]# rpm -qa subversion subversion--. ...
- netty系列之:搭建自己的下载文件服务器
目录 简介 文件的content-type 客户端缓存文件 其他HTTP中常用的处理 文件内容展示处理 文件传输进度 总结 简介 上一篇文章我们学习了如何在netty中搭建一个HTTP服务器,讨论了如 ...
- netty系列之:搭建HTTP上传文件服务器
目录 简介 GET方法上传数据 POST方法上传数据 POST方法上传文件 总结 简介 上一篇的文章中,我们讲到了如何从HTTP服务器中下载文件,和搭建下载文件服务器应该注意的问题,使用的GET方法. ...
- netty系列之:使用netty搭建websocket客户端
目录 简介 浏览器客户端 netty对websocket客户端的支持 WebSocketClientHandshaker WebSocketClientCompressionHandler netty ...
- Netty 系列(三)Netty 入门
Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...
- Netty系列之Netty百万级推送服务设计要点
1. 背景 1.1. 话题来源 最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题.问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为 ...
- 【netty】Netty系列之Netty百万级推送服务设计要点
1. 背景 1.1. 话题来源 最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题.问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为 ...
- 【netty】(2)---搭建一个简单服务器
netty(2)---搭建一个简单服务器 说明:本篇博客是基于学习慕课网有关视频教学.效果:当用户访问:localhost:8088 后 服务器返回 "hello netty"; ...
随机推荐
- 网页兼容最新IE声明meta方法
第三种,总是使用最新版本文档模式. 以下是例子: <meta http-equiv="X-UA-Compatible" content="IE=edge" ...
- Jmeter系列(34)- Jmeter优化常识
Jmeter UI页面是调试脚本的,运行脚本使用命令行运行:Windows使用batch,Linux使用shell Jmeter减少使用各类监听控件,吃内存.CPU:用后置处理去拿log文件,生成图表 ...
- Shell系列(5)- 输出输入重定向及wc命令
输出重定向: 在Linux当中,0代表输入:1代表正确输出:2代表错误输出 类型 符号 作用 正确输出重定向 命令 > 文件 以覆盖得方式,把命令得正确输出,输出到指定文件或设备当中 命令 &g ...
- JDBC-2(CRUD)
3.PreparedStatement实现CRUD 3.1 操作和访问数据库 数据库连接被用于向数据库服务器发送命令和SQL语句,接受数据库服务器返回的结果.(一个数据库连接就是也给Socket连接) ...
- jmeter长时间压测
如何进行24h小时的压测? 长时间压测注意事项 生成报告文件过大 https://www.cnblogs.com/SunshineKimi/p/12298668.html
- 《DotNet Web应用单文件部署系列》三、混淆dll文件
众所周知,C#编译后的dll文件可被反编译,网上搜索"C# 反编译"会出现一大堆资料.为了提高反编译成本,我们必须对dll文件进行混淆处理. 目前,C#混淆工具很多,我推荐obfu ...
- 域名系统-DNS
域名系统DNS 域名系统DNS(Domain Name System)是互联网使用的命名系统,用来把便于人们使用的机器名转化为IP地址,域名系统就是名字系统. 很多应用层的软件经常直接使用DNS.DN ...
- JOIN US | SphereEx 精英集结
新环境.新气象,SphereEx 欢迎志同道合的你加入! 关于 SphereEx 北京思斐软件技术有限公司(sphere-ex.com),是一家致力于构建新型分布式数据基础设施的公司,秉承开源.共享. ...
- 题解 2020.10.24 考试 T3 数列
题目传送门 题目大意 给出一个数 \(n\),你要构造一个数列,满足里面每个数都是 \(n\) 的因子,且每一个数与前面不互质的个数不超过 \(1\).问有多少种合法方案. 保证 \(n\) 的不同质 ...
- 纯前端H5小应用_localStorage存储
开发缘由[需求发现和分析] 想要送朋友一个礼物,但是想了想,街上买的东西,em~,我们这样的猿确实不会选礼物啊,由此就想利用自己手中的工具和知识做点有用的东西吧,抱枕是礼物,钢笔是礼物,电子产品也是礼 ...