netty系列之:手持framecodec神器,创建多路复用http2客户端
简介
在之前的文章中,我们实现了支持http2的netty服务器,并且使用支持http2的浏览器成功的进行访问。虽然浏览器非常通用,但是有时候我们也需要使用特定的netty客户端去和服务器进行通信。
今天我们来探讨一下netty客户端对http2的支持。
配置SslContext
虽然http2并不强制要求支持TLS,但是现代浏览器都是需要在TLS的环境中开启http2,所以对于客户端来说,同样需要配置好支持http2的SslContext。客户端和服务器端配置SslContext的内容没有太大的区别,唯一的区别就是需要调用SslContextBuilder.forClient()而不是forServer()方法来获取SslContextBuilder,创建SslContext的代码如下:
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();
如果使用SSL,那么ssl handler必须是pipline中的第一个handler,所以将SslContext加入到pipline中的代码如下:
ch.pipeline().addFirst(sslCtx.newHandler(ch.alloc()));
客户端的handler
使用Http2FrameCodec
netty的channel默认只能接收ByteBuf消息,对于http2来说,底层传输的是一个个的frame,直接操作底层的frame对于普通程序员来说并不是特别友好,所以netty提供了一个Http2FrameCodec来对底层的http2 frame进行封装成Http2Frame对象,方便程序的处理。
在服务器端我们使用Http2FrameCodecBuilder.forServer()来创建Http2FrameCodec,在客户端我们使用Http2FrameCodecBuilder.forClient()来创建Http2FrameCodec:
Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient()
.initialSettings(Http2Settings.defaultSettings())
.build();
然后将其加入到pipline中即可使用:
ch.pipeline().addLast(http2FrameCodec);
Http2MultiplexHandler和Http2MultiplexCodec
我们知道对于http2来说一个TCP连接中可以创建多个stream,每个stream又是由多个frame来组成的。考虑到多路复用的情况,netty可以为每一个stream创建一个单独的channel,对于新创建的每个channel来说,都可以使用netty的ChannelInboundHandler来对channel的消息进行处理,从而提升netty处理http2的效率。
而这个对stream创建新channel的支持,在netty中有两个专门的类,他们是Http2MultiplexHandler和Http2MultiplexCodec。
他们的功能是一样的,Http2MultiplexHandler继承自Http2ChannelDuplexHandler,它必须和 Http2FrameCodec一起使用。而Http2MultiplexCodec本身就是继承自Http2FrameCodec,已经结合了Http2FrameCodec的功能。
public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler
@Deprecated
public class Http2MultiplexCodec extends Http2FrameCodec
但是通过检查源代码,我们发现Http2MultiplexCodec是不推荐使用的API,所以这里我们主要介绍Http2MultiplexHandler。
对于Http2MultiplexHandler来说,每次新创建一个stream,都会创建一个新的对应的channel,应用程序使用这个新创建的channel来发送和接收Http2StreamFrame。
新创建的子channel会被注册到netty的EventLoop中,所以对于一个有效的子channel来说,并不是立刻就会被匹配到HTTP/2 stream上去,而是当第一个Http2HeadersFrame成功被发送或者接收之后,才会触发Event事件,进而进行绑定操作。
因为是子channel,所以对于connection level的事件,比如Http2SettingsFrame 和 Http2GoAwayFrame会首先被父channel进行处理,然后再广播到子channel中进行处理。
同时,虽然Http2GoAwayFrame 和 Http2ResetFrame表示远程节点已经不再接收新的frame了,但是因为channel本身还可能有queue的消息,所以需要等待Channel.read()为空之后,才会进行关闭操作。
另外对于子channel来说,因为不能知道connection-level流控制window,所以如果有溢出的消息会被缓存在父channel的buff中。
有了Http2MultiplexHandler,将其加入client的pipline就可以让客户端支持多路的channel了:
ch.pipeline().addLast(new Http2MultiplexHandler(new SimpleChannelInboundHandler() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
// 处理inbound streams
log.info("Http2MultiplexHandler接收到消息: {}",msg);
}
}))
使用子channel发送消息
从上面的介绍我们知道,一旦使用了Http2MultiplexHandler,那么具体的消息处理就是在子channel中了。那么怎么才能从父channel中获取子channel,然后使用子channel来发送信息呢?
netty提供Http2StreamChannelBootstrap类,它提供了open方法,来创建子channel:
final Http2StreamChannel streamChannel;
try {
if (ctx.handler() instanceof Http2MultiplexCodec) {
streamChannel = ((Http2MultiplexCodec) ctx.handler()).newOutboundStream();
} else {
streamChannel = ((Http2MultiplexHandler) ctx.handler()).newOutboundStream();
}
我们要做的就是调用这个方法,来创建子channel:
final Http2StreamChannel streamChannel = streamChannelBootstrap.open().syncUninterruptibly().getNow();
然后将自定义的,专门处理Http2StreamFrame的Http2ClientStreamFrameHandler,添加到子channel的pipline中即可:
final Http2ClientStreamFrameHandler streamFrameResponseHandler =
new Http2ClientStreamFrameHandler();
streamChannel.pipeline().addLast(streamFrameResponseHandler);
准备完毕,构建http2消息,使用streamChannel进行发送:
// 发送HTTP2 get请求
final DefaultHttp2Headers headers = new DefaultHttp2Headers();
headers.method("GET");
headers.path(PATH);
headers.scheme(SSL? "https" : "http");
Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, true);
streamChannel.writeAndFlush(headersFrame);
总结
以上就是使用netty的framecode构建http2的客户端和服务器端进行通信的基本操作了。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/32-netty-http2client-framecodec/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
netty系列之:手持framecodec神器,创建多路复用http2客户端的更多相关文章
- netty系列之:性能为王!创建多路复用http2服务器
目录 简介 多路复用的基础 多路复用在server端的使用 配置TLS处理器 配置clear text upgrade 总结 简介 在之前的文章中,我们提到了在netty的客户端通过使用Http2Fr ...
- 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?
[读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...
- 3. 彤哥说netty系列之Java BIO NIO AIO进化史
你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...
- 5. 彤哥说netty系列之Java NIO核心组件之Channel
你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...
- 7. 彤哥说netty系列之Java NIO核心组件之Selector
--日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第七篇. 简介 上一章我们一起学习了Java NIO的核心组件Buffer,它通常跟Channel一起使用,但是它们在网络IO中又该如何 ...
- Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化
Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...
- Netty 系列(三)Netty 入门
Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...
- Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程 第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...
- 2. 彤哥说netty系列之IO的五种模型
你好,我是彤哥,本篇是netty系列的第二篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别. ...
随机推荐
- hdu 2147 kiki's game(DP(SG)打表找规律)
题意: n*m的棋盘,一枚硬币右上角,每人每次可将硬币移向三个方向之一(一格单位):左边,下边,左下边. 无法移动硬币的人负. 给出n和m,问,先手胜还是后手胜. 数据范围: n, m (0<n ...
- 前端面试手写代码——模拟实现new运算符
目录 1 new 运算符简介 2 new 究竟干了什么事 3 模拟实现 new 运算符 4 补充 预备知识: 了解原型和原型链 了解this绑定 1 new 运算符简介 MDN文档:new 运算符创建 ...
- Python展示文件下载进度条
前言 大家在用Python写一些小程序的时候,经常都会用到文件下载,对于一些较小的文件,大家可能不太在乎文件的下载进度,因为一会就下载完毕了. 但是当文件较大,比如下载chromedriver的时候, ...
- linux初中级命令语言
Linux:开源免费.大部分软件都可以自由获取,同样功能的软件选择较少.主要是字符模式,命令行界面且发行版本较多,难以集中攻击. Xshell与xftp是什么? xshell是一个客户端软件,我们本地 ...
- SpringCloud升级之路2020.0.x版-30. FeignClient 实现重试
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 需要重试的场景 微服务系统中,会遇到在线发布,一般的发布更新策略是:启动一个新的,启动成功 ...
- PTA 7-3 Windows消息队列 (25分)
PTA 7-3 Windows消息队列 (25分) 消息队列是Windows系统的基础.对于每个进程,系统维护一个消息队列.如果在进程中有特定事件发生,如点击鼠标.文字改变等,系统将把这个消息加到队列 ...
- CF285D.D. Permutation Sum
CF285D. Permutation Sum 题目 大意 寻找a,b两个排列从0到n-1,有c[i]=(a[i]+b[i])%n+1,使得c[i]也为全排列的排列方式 思路 a中元素和b中元素的对应 ...
- Python 中的反转字符串:reversed()、切片等
摘要:以相反的顺序反转和处理字符串可能是编程中的一项常见任务.Python 提供了一组工具和技术,可以帮助您快速有效地执行字符串反转. 本文分享自华为云社区<Python 中的反转字符串:rev ...
- 从华为新发布的WeAutomate 3.0,看RPA如何在政企领域落地生长
文/王吉伟 11月11日,是电商的重要节日.即便今年双11的气氛不如往年浓烈,人们依旧关注双11厂商战报,关注购物车里的商品有没有降价. 当然在RPA领域,大家除了关注双11的商品价格,更关注华为RP ...
- Nginx server_name翻译
http://nginx.org/en/docs/http/server_names.html#regex_names 匹配优先顺序 精确名称,无通配符,无正则. 以星号开头的最长的通配符名称,例如& ...