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"; ...
随机推荐
- React框架的基本使用和了解
React: React详解: 安装react 脚手架工具: npm install -g create-react-app create-react-app 项目名称 cnpm react-dom ...
- Stage 1 项目需求分析报告
迷你商城后台管理系统-- 需求分析 1. 引言 作为互联网热潮的崛起,消费者们的普遍差异化,实体商城要想在互联网的浪潮中继续发展,就需要制定出针对用户以及消费者的消费习惯以及喜爱品种的消费方案.从而企 ...
- CPU到底是什么东西?它为什么能够执行数学运算?
CPU到底是什么东西?它为什么能够执行数学运算? 本文地址http://yangjianyong.cn/?p=20转载无需经过作者本人授权 简单的物理电路 先来看一张初中学过的物理电路图: 从图中我们 ...
- Mysql Navicate 基础操作与SQL语句 版本5.7.29
SQL数据的增删改查:此部分所有SQL语句在navicat中与mysql命令行执行效果一样,只是mysql服务端在命令行执行,而navicat只是在客户端的图形化打开操作. 一.进入数据库 .连接数据 ...
- Jmeter监控技术实战
性能测试中监控的意义 为性能分析提供依据 监控方案 serverAgent jmeter的插件,监控颗粒度不高,界面简陋 服务器中启动 jmeter中添加插件 Nmon Grafana 优秀监控方案所 ...
- webpack工具学习 构建简单vue项目(不依赖vue-cli) webpack4.0
目的用webpack构建简单前端项目 1.npm init (npm init -y) 形成package.json 2.npm install --save-dev webpack 形成 n ...
- 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 百篇博客分析OpenHarmony源码 | v15.03
百篇博客系列篇.本篇为: v15.xx 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...
- P4292-[WC2010]重建计划【长链剖分,线段树,0/1分数规划】
正题 题目链接:https://www.luogu.com.cn/problem/P4292 题目大意 给出\(n\)个点的一棵树,然后求长度在\([L,U]\)之间的一条路径的平均权值最大. 解题思 ...
- 阿里云ECS服务器Centos中安装SQL Server(破解内存限制)
前言 前段时间赶上阿里云618活动入手了一个低配的Linux服务器,供自己学习使用,在安装SQL Server中遇到了很多小问题,查阅很多博客结合自身遇到的问题做个总结. 安装过程 1.下载阿里云在线 ...
- windows下如何查看所有端口及占用
1.在windows下查看所有端口: 先点击电脑左下角的开始,然后选择运行选项,接着我们在弹出的窗口中,输入[cmd]命令,进行命令提示符. 然后我们在窗口中输入[netstat -ano]按下回车, ...